反射

  • 为什么要学习反射?

首先说一点:反射是比较低级的一个知识,如果是单纯的撸码来是实现功能的话,反射基本用不到,而且反射的性能呢也肯定不怎么好的,但是我个人觉得反射是一个很重要的知识点,在我老早学习的时候就是这么认为的,主要的原因有2点:

1,java的编译和运行。java是强类型语言,运行java每一个对象都会出现2个种类型:编译时类型和运行时类型,好多的知识点都只是关于编译时类型的,比如说多态,比如说泛型。java在运行时那个对象是自己实实在在的那个对象,并不是说编译时候的类型,所以要研究好反射,就可以得到这个实实在在的那个对象了,这对于理解java的工作原理是很重要的。举个例子:如果程序需要在运行时发现类和对象的真实信息,而且在编译阶段就根本不知道这个对象确切的属于哪个类,那么程序只能依靠运行时信息来发现该对象和类的真实情况,就必须要用反射了。注意:在反射中没有简单数据类型,所有的编译时类型都是对象。反射把编译时应该解决的问题留到了运行时。

2,框架的底层。我记得在老早的时候,几个搞android的是朋友就说框架不好的,怎么怎么地的,性能了什么的,不好了怎么怎么样,我觉得很不可理解。在以前我个人也觉得框架是一个比较深奥的东西,现在看来框架其实也没有什么的。说白了,框架就2个东西,一个是解析配置文件,一个是反射动态来获得对象。先说配置文件:这个可能就是j2ee和.net最大的区别,j2ee领域存在大量的配置,不管是XML配置文件,还是通过注解方式,其实就是把一些不关于业务逻辑的代码抽离出来,方便以后的改动,如果你不这样子实现的话,那只能硬码写4了,几乎没有任何可变性,那就是不是框架了。然后动态获得对象,除去性能了,安全了,可维护等等多个有利条件,框架就是把所谓的一些通用的步骤整理起来,这样子就彻底的解放了码农,码农要做的只是把自己的业务逻辑代码放进去,这样子就可以跑了,那么问题来了:框架都是T前写好的jar文件,已经编译过的,预先已经定义好了好多的对象在那里运行呢,那么我们自己放进去的对象如何才能插入到框架里面跑起来呢?很简单:反射。所以要认真的研究好反射,这样子对于理解框架的底层很重要的。说实际的,现在框架对于我来说并不是什么很高深的东西了,无非就是通过一些很好的设计模式,来完成一些通用功能。我们在使用的时候,写一些配置文件和自己的业务逻辑代码,框架会根据配置文件,来解析XML或者注解,把我们的代码参与到运行阶段,这就是任何一个框架的原理。


  • Class类

定义:在程序运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识。虚拟机利用类型标识选用相应的方法执行。可以通过专门的类访问这些信息。保存这些信息的类被称为Class(类)。

每一个类被加载之后,系统都会为该类生成一个Class对象,通过该Class对象就可以访问JVM中的这个类,说白了,这个Class就是JVM的2进制文件,JDK中是这么说:Class就是表示Class 对象建模的类,用这个类在创建对象的。对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。Class 对象只能由系统建立对象,一个类在 JVM 中只会有一个Class实例,每个类的实例都会记得自己是由哪个 Class 实例所生成。

注意:对象照镜子后可以得到的信息:某个类的数据成员名、方法和构造器、某个类到底实现了哪些接口。这句话其实也说出了反射的最准备和直观的意义:就是通过JVM中的这个类的一部分来获得所有的关于这个类的信息,并为之转换成一个其他的可以使用的类型。

  

现在很明显了,玩反射第一步就是要或者这个对象或者是类在JVM中的那个Class对象,然后调用他的方法来获得其他的东西。那么如何获得Class对象?有3种方式:
    (1)通过类名获得类对象 Class c1 = String.class; //类.Class;
    (2)通过对象获得类对象 Class c2 = s.getClass(); //类对象.getClass();
    (3)通过Class.forName(“类的全名”)来获得类对象    //Class.forName(包名.类名);        Class c3 = Class.forName(“Java.lang.String”);//这会强行加载类到内存里,前两种不加载。 注:第三种方式是最常用最灵活的方式。第三种方式又叫强制类加载。

关于这3种方式的取舍:如果现在我们得到的是一个对象,那么就只能用getClass()这个方法了,如果现在给了类名了,那么我们就可以使用这个类名打点.class这个属性,其实一般使用这个最多。如果我们现在拿到的只是一个普通的字符串,我们需要获得这个字符串对应的Class对象,就只能使用Class类的静态方法:ForName()了。

package linkin;


public class Linkin 
{
	
	public static void main(String[] args) throws ClassNotFoundException
	{
		//通过类的Class属性来获取,这个方法最安全可靠,程序的性能也是最高的
		Class<String> class1 = String.class;
		//通过一个对象来获取
		Class<String> class2 = (Class<String>) "LinkinPark...".getClass();
		//通过Class类的静态方法来获取,这里要抛异常的
		Class<String> class3 = (Class<String>) Class.forName("java.lang.String");
	}
}


  • 一旦获得某个类所对应的Class对象后,就可以调用Class类的方法来获得这个类的所有的真实信息了,包括构造器,属性,方法,注解,泛型。
构造器:


方法:


属性:


注解:


declared 申明的,公然的,容易发现上面API中,后面的方法都是在前面的方法签名上加了这个单词,意思就是可以获取所有的对应的成员,与这个成员的访问权限无关,要是直接使用前面的话,就只能获取这个类对应的用public来修饰的成员。此外,其他的方法不是经常使用,比如获得有关内部类的情况,继承的是父类,实现的接口,类的修饰符,所在包,类名等等,要是用的话自己去翻API。代码都比较简单的,这里就不敲了。注意一点:对于只能在源代码上保留的注释,使用运行时获得Class对象无法访问该注释对象。


  • 通过Class对象可以得到大量的Method,Constructor,Field等对象,然后可以通过这些对象来执行实际的功能,比如说调用方法,创建实例。
1,创建对象。
通过反射来生成对象有2中方式:(1),使用Class对象的newInstance()方法。要求该CLass类必须要有默认的构造器。这里创建实例实际调用的就是这个默认的构造器。(2),先使用Class对象来获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。这种方式可以使用指定的构造器来创建实例。

package linkin;

import java.lang.reflect.Constructor;


public class Linkin 
{
	private String name;
	
	public Linkin()
	{
		
	}
	
	public Linkin(String name)
	{
		this.name = name;
	}
	
	public String getName()
	{
		return name;
	}

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

	public static void main(String[] args) throws Exception
	{
		Class<Linkin> class1 = Linkin.class;
		//调用默认的构造器
		class1.newInstance();
		//调用自己指定的构造器,注意后面方法的参数是传入的参数类型。
		Constructor<Linkin> cons = class1.getConstructor(String.class);
		cons.newInstance("LinkinPark");
	}
}


2,调用方法
调用方法就一种方式,先获取这个方法,然后再用这个方法的invoke()方法。

package linkin;

import java.lang.reflect.Method;


public class Linkin 
{
	private String name;
	
	public Linkin()
	{
		
	}
	
	public Linkin(String name)
	{
		this.name = name;
	}
	
	public String getName()
	{
		return name;
	}

	public void setName(String name)
	{
		this.name = name;
	}
	
	public static void test(String str)
	{
		System.out.println("这里是该类的静态方法:"+str);
	}
	
	public static void main(String[] args) throws Exception
	{
		Class<Linkin> class1 = Linkin.class;
		Linkin linkin = class1.newInstance();
		Method mth = class1.getMethod("setName", String.class);
		mth.invoke(linkin, "LinkinPark..");
		System.out.println(linkin.getName());
		Method mth1 = class1.getMethod("test", String.class);
		//要是这个方法时一个静态方法,那么传入主调是一个null就好了
		mth1.invoke(null, "Binger");
	}
}


3,访问属性值
这个也很简单,通过Class对象可以获得对象的属性。


如果是基本类型的话,就在get和set后面加上相应的XXX类型就可以。

package linkin;

import java.lang.reflect.Field;

class Linkin 
{
	public String name = "LinkinPark...";
	private int age;
	private String andress = "hangzhou";
	
	public String getAndress()
	{
		return andress;
	}

	public void setAndress(String andress)
	{
		this.andress = andress;
	}

	public String getName()
	{
		return name;
	}

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

	public int getAge()
	{
		return age;
	}

	public void setAge(int age)
	{
		this.age = age;
	}
	
}

public class Test
{
	public static void main(String[] args) throws Exception
	{
		Class<Linkin> class1 = Linkin.class;
		Linkin linkin = class1.newInstance();
		Field name = class1.getField("name");
		String nameValue = (String) name.get(linkin);
		System.out.println(nameValue);
		//不管限定符,可以获取所有的属性
		Field andress = class1.getDeclaredField("andress");
		//如果是私有的,或者是其他不能访问的,这里就设置可以到达,这样子就可以访问了
		andress.setAccessible(true);
		String andressValue = (String) andress.get(linkin);
		System.out.println(andressValue);
		Field age = class1.getDeclaredField("age");
		age.setAccessible(true);
		age.setInt(linkin, 25);
		System.out.println(linkin.getAge());
	}
}




总结:以上3个知识点经常用到,有2点注意,1,一旦在调反射得到某一个成员的时候说没有这个成员(Exception in thread "main" java.lang.NoSuchFieldException: age),那么就把原来的方法上加上declared ,这样子就不会去管这个成员上面的限定符了。2,要是说某一个私有的东西不能到达或者访问,(Class linkin.Test can not access a member of class linkin.Linkin with modifiers "private")那么就设置这个成员的setAccessible()为true。

另外还有2个东西可以了解下,不是很常用到:

1,操作数组:java.lang.reflect下还提供了一个Array类,这个类的对象可以代表所有的数组。下面的方法全是都是静态的。

  这个方法没有使用泛型,自己可以封装下:public static <T> T[] newInstance(Class<T> type,int len);


package linkin;

import java.lang.reflect.Array;

public class Test
{
	public static void main(String[] args) throws Exception
	{
		Object arr = Array.newInstance(String.class, 5);
		//下面的get和set方法全部都是静态的,使用起来不是很方便耶
		Array.set(arr, 0, "LinkinPark...");
		Array.set(arr, 1, "Binger...");
		String linkin = (String) Array.get(arr, 0);
		String binger = (String) Array.get(arr, 1);
		System.out.println(linkin+":"+binger);
		//这里强转上面那个object对象,就可以使用循环了。这个方法有点恶心,既然有了泛型了,为什么不整成泛型的呢?
		String[] huhu = (String[]) arr;
		for (String string : huhu)
		{
			System.out.println(string);
		}
	}
}




2,操作泛型:通过在反射中使用泛型,可以避免使用反射生成的对象做强转。

package linkin;

public class Test
{
	public static <T> T getInstance(Class<T> cls) throws InstantiationException, IllegalAccessException
	{
		return cls.newInstance();
	}
	public static void main(String[] args) throws Exception
	{
		Class<String> cls = String.class;
		//这里要强转
		String str1 = (String) cls.newInstance();
		//这里不需要强转了
		String str2 = Test.getInstance(String.class);
	}
}




3,使用反射来获取泛型信息


前面所讲的获取属性后,属性里面有一个方法:getType();这个方法只是对普通的属性有效,要是这个属性使用了泛型的话,就要使用getGenericType()方法。获得对应的Type类型后,就可以掉上面的方法来获得具体的泛型类型了,其实这也是就hibernate中根据配置文件来读取泛型实体属性的类型的原理。


package linkin;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;

public class Test
{
	private Map<String,Integer> map;
	
	public static void main(String[] args) throws Exception
	{
		Class<Test> cla = Test.class;
		Field map = cla.getDeclaredField("map");
		Class<?> type1 = map.getType();
		System.out.println(type1);//interface java.util.Map
		Type type2 = map.getGenericType();
		if(type2 instanceof ParameterizedType)
		{
			//强转成为泛型参数化类型
			ParameterizedType pType2 = (ParameterizedType) type2;
			//原始类型
			Type rType = pType2.getRawType();
			System.out.println(rType);//interface java.util.Map
			Type[] tTypes = pType2.getActualTypeArguments();
			for (Type type : tTypes)
			{
				//class java.lang.String    class java.lang.Integer
				System.out.println(type);
			}
		}
		else
		{
			System.out.println("获取属性的泛型类型出错了吆...");
		}
	}
}




原文地址:https://www.cnblogs.com/LinkinPark/p/5233121.html