当前位置:阳光沙滩 >Java > 查看文章
阿里云优惠码

JAVA中的反射

标签:, Java

1.前言

之前发的文章,都是部分的知识点,所以,这一篇还是把反射相关的知识列举出来吧,貌似在面试中没问多少,嘻嘻。一般开发可能用不上,用在那里呢,比如数据库JDBC中、编写框架的时候。

 

2.一、什么是反射

反,相反,射,映射。相当于逆映射。我们学习JAVA类时,都是从学习构造方法,方法,属性等入手,这是正。而现在是我们要获取这些类的对象中的各种方法属性,这就是反射。或者说,反射就是类中的方法、属性与Java类的映射。

用于获取完全未知的字节码文件的构造方法,方法,属性等,貌似有点像反汇编吧。

 

3.二、Class类

Class类,这个类是大写的开头哦。那么这个类是什么呢?它是在java.lang包中的一个类,提供了获取类构造方法,方法,属性等方法。操作的对象是字节码对象。

什么是字节码,这应该都知道吧,通俗点说就是.class文件,再通俗点说,就是java编译后的文件,让JVM解析执行的文件。

获取Class对象的方式

|--通过Object的getClass方法来获取:Class<?> cls = new Person().getClass();

|--直接用类名.class来获取,这个很少且,一般在框架开发时常见: Class<?> cls = Person.class;

|--用这个Class提供的方法:forName(String className):Class<?> cls = Class.forName("Person");

最后一个是最常用的方法(^_^)

 

九个预定义的Class

|--八个基本数据类型:byte.class, short.class, int.class, long.class, float.class, double.class, char.class, boolean.class

|--一个void字节码void.class

 

Class类中的主要方法:

方法摘要
static Class<?> forName(String className)
返回与带有给定字符串名的类或接口相关联的 Class 对象。
static Class<?>
forName(String name, boolean initialize, ClassLoader loader)
使用给定的类加载器,返回与带有给定字符串名的类或接口相关联的 Class 对象。
 Class<?>[] getClasses()
返回一个包含某些 Class 对象的数组,这些对象表示属于此 Class 对象所表示的类的成员的所有公共类和接口。
 Constructor<T> getConstructor(Class<?>... parameterTypes)
返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。
 Constructor<?>[] getConstructors()
返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。
 Field getField(String name)
返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。
 Field[] getFields()
返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。
 Class<?>[] getInterfaces()
确定此对象所表示的类或接口实现的接口。
 Method getMethod(String name, Class<?>... parameterTypes)
返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
 Method[] getMethods()
返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。
 String getName()
String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。
 Package getPackage()
获取此类的包。
 Class<? super T> getSuperclass()
返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class
 boolean isArray()
判定此 Class 对象是否表示一个数组类。
 boolean isAssignableFrom(Class<?> cls)
判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同,或是否是其超类或超接口。
 boolean isEnum()
当且仅当该类声明为源代码中的枚举时返回 true。
 boolean isInstance(Object obj)
判定指定的 Object 是否与此 Class 所表示的对象赋值兼容。
 boolean isInterface()
判定指定的 Class 对象是否表示一个接口类型。
 T newInstance()
创建此 Class 对象所表示的类的一个新实例。

4.三、用于实例化对象

我们一般的”正射“实例化对象的方法是用new关键字来实例化。比如说 Person person = new Person();

那么用反射的方式如何实例化对象呢?其实是这样子的:

//先获取到Class对象

Class<?> cls = Class.forName("Person");

//用newInstance方法来实例化对象,返回的是一个Object型对象。

Object obj = cls.newInstance();

具体我们完整地用代码体现一下吧:

public class Demo{
	public static void main(String[]args)throws Exception{
		//获取Class对象
		Class<?> cls = Class.forName("SOB");
		//实例化对象
		Object obj = cls.newInstance();
		//向下转型为这个SOB类
		SOB sob = (SOB)obj;
		//输出一下验证呗,嘻嘻!
		System.out.println(sob);
	}
}

class SOB{
	
	//定义变量
        public String pubVar1 = "varialbe....1";//定义这个公开的变量
        public String pubVar2 = "varialbe.....2";
	private String membName = "sunofbeaches";//给个初始值吧,稍后不用先传值。
	private int ID = 0;
	
	//定义构造方法,方便后面说这个获取构造方法
	public SOB(){}
	public SOB(String membName){
		this.membName = membName;
	}
	//理论上不需要指定ID的,这里便于后面的说明,添加一个两个参数的构造方法吧
	public SOB(String membName,int ID){
		this.membName = membName;
		this.ID = ID;
	}
	
	public void show(){
		System.out.println(this.membName+"....."+this.ID);
	}
	
	public String toString(){
		return this.membName+" : " + this.ID; 
	}
	
}

 

运行结果如下:

%e6%88%aa%e5%9b%be00

由结果可以看出,这个对象是非常成功地实例化的哦,所以呢,这种方法也是行的哈。有没有感觉这方法比起直接new来更复杂点呢,有的哈,那么,为什么还要这样做呢?

我们常说,这个对于程序的开发模式,尽量减少这个耦合,那么减少耦合的最好方法就是使用接口,但是,但是,但是这个接口也无法逃避这个new,所以造成耦合的关键原因是new啦,哈哈!

5.四、Constructor类

Constructor,指挥的意思,这里指构造者。它是java.lang.reflect包中的一个类。

上面,我们已经通过了newInstance这个方法来实例化对象,但那只是没有参数的,也就是空参的构造方法实例化的,那么,如果Class对象没有空参的构造方法呢,这个时候,我们就需要获取到这个Class对象的对应构造方法,传参数来实例化了。

那好,我们这怎么玩呢,先看看怎么获取构造方法吧。

在前面Class类中,我们提到这个类中的主要方法,那么有两个是方法是用来获取Class对象中的构造方法的。

 

第一个是返回一个构造方法,这个构造方法需要指定这个参数:

Constructor<T> getConstructor(Class<?>... parameterTypes)
返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。

 

第二个是返回一坨构造方法

Constructor<?>[] getConstructors()
返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。

 

具体用法如下(省略这个SOB类哈,方便大家看,其实上面的代码看一次就可以有个印象了):

import java.lang.reflect.*;
public class Demo{
	public static void main(String[]args)throws Exception{
			
		Class<?> cls = Class.forName("SOB");
		//获取所有的构造方法
		Constructor cons[] = cls.getConstructors();
		for(int i = 0;i<cons.length;i++){
			System.out.println(cons[i]);
		}
		System.out.println("........华丽的分割线.........");
		//获取指定参数的构造方法
		//参数用数组的形式
		Class arg[] = new Class[]{String.class,int.class} ;
		Constructor con1 = cls.getConstructor(arg);
		System.out.println("String.class,int.class---->"+con1);
		//直接写参数的形式
		Constructor con2 = cls.getConstructor(String.class);
		System.out.println("String.class---->"+con2);
				
	}
}

运行结果如下:

%e6%88%aa%e5%9b%be01

看到了吧,看到这些获取到的构造方法,我们又可以快乐地玩耍,高兴地装逼去了。

继续,我们要实例化对象哦,嘻嘻!

我们可以毫不惊讶地发现,这个在Construcotr类中,竟然也有一个newInstance哦,是的,你真的没有看错,还真的有一个。

那好,有就行了,话不多说,马上来实例化一下玩玩。

import java.lang.reflect.*;
public class Demo{
	public static void main(String[]args)throws Exception{

		Class<?> cls = Class.forName("SOB");
		//获取所有的构造方法
		Constructor cons[] = cls.getConstructors();
		for(int i = 0;i<cons.length;i++){
			System.out.println(cons[i]);
		}
		System.out.println("........华丽的分割线.........");
		//获取指定参数的构造方法
		//参数用数组的形式
		Class arg[] = new Class[]{String.class,int.class} ;
		Constructor con1 = cls.getConstructor(arg);
		System.out.println("String.class,int.class---->"+con1);
		//直接写参数的形式
		Constructor con2 = cls.getConstructor(String.class);
		System.out.println("String.class---->"+con2);
		System.out.println("........实例化对象调用show方法结果.........");
		//实例化对象
		Object obj = con1.newInstance("阳光沙滩",1);
		SOB sob = (SOB)obj;
		sob.show();		
	}
}

结果和我想的一样,竟然成功啦,哈哈!好玩吧,嘻嘻!

%e6%88%aa%e5%9b%be021

好啦,两中获取这个构造方法的方式都搞定了,那我们继续往下吧!

6.五、Field类

看到这个Field大家有没有联想到这个多线程中的yield呢?这有关系吗?其实没太大有关系吧,只是,只是,只是我以前,甚至现在经常输入时写错,嘻嘻,如果后面真有写错,抱歉啦,嘻嘻!

好,回到正题上,这个Field类,它代表着最类中广大成员变量。

在Class类中,有这个方法可以获取到这个变量

Field getField(String s);//只能获取公有和父类中公有

Field getDeclaredField(String s);//获取该类中任意成员变量,包括私有

setAccessible(ture);//如果是私有字段,要先将该私有字段进行取消权限检查的能力。也称暴力访问。

set(Object obj, Object value);//将指定对象变量上此Field对象表示的字段设置为指定的新值。

Object get(Object obj);//返回指定对象上Field表示的字段的值。

直接来例子啦:

import java.lang.reflect.*;
public class Demo{
	public static void main(String[]args)throws Exception{
	
		Class<?> cls = Class.forName("SOB");
		//访问变量,需要要对象吧
		SOB sob = (SOB)cls.newInstance();
		
		//获取所有父类和子类中的Public变量
		Field fields[] = cls.getFields();
		for(Field field:fields){
			System.out.println(field);
		}
		System.out.println("----------华丽的分割线------------");
		//获取指定名字的成员变量
		Field fVariable = cls.getField("pubVar1");
		System.out.println(fVariable);
		//修改这个值哈
		fVariable.set(sob,"changeValue");
		System.out.println(sob.pubVar1);
		
		
		
		//访问私有的,这个方法是可以访问任意的,包括私有的。
		Field fMembName = cls.getDeclaredField("membName");
		System.out.println(fMembName);
		
		//暴力修改这个私有的值,先修改这个通道属性
		fMembName.setAccessible(true);
		//修改值
		fMembName.set(sob,"阳光沙滩");
		System.out.println(fMembName.get(sob));	
		
	}
}

运行结果如下:

%e6%88%aa%e5%9b%be031

7.六、Method类

同样的,说完了成员变量,构造方法,当然还要说说这个方法啦。对于方法的话,也是类似的方法,找到Class类中的对应方法看看就OK啦,现在这个是方法的,那么我们就找出方法相关的方法哈,嘻嘻。或者说,我们找出函数相关的方法吧。

Method[] getMethods();//只获取公共和父类中的方法。
Method[] getDeclaredMethods();//获取本类中包含私有。
Method getMethod("方法名",参数.class(如果是空参可以写null));
Object invoke(Object obj ,参数);//调用方法
如果方法是静态,invoke方法中的对象参数可以为null。

 

先说说获取方法吧:

同样,获取方法也是有两种,一种是获取所有的方法,另外一种是获取指定参数的方法。与这个构造方法的获取类似,只是参数不一样,因为方法有多个,所以名字不一样,如果名字一样,方法的参数可能不一样,因为重载嘛,是吧!

import java.lang.reflect.*;
public class Demo{
	public static void main(String[]args)throws Exception{
		
		Class<?> cls = Class.forName("SOB");
		
		//获取这个所有的(public)方法
		Method methods[] = cls.getMethods();
		for(Method method:methods){
			System.out.println(method);
		}
		System.out.println("----------华丽的分割线------------");
		//获取这个所有的(public&private)方法
		Method methods1[] = cls.getDeclaredMethods();
		for(Method method:methods1){
			System.out.println(method);
		}
		
		System.out.println("----------华丽的分割线------------");
		//Class <?> arg[] = new Class[]{null};
		//获取这个指定的方法
		Method getMeth = cls.getMethod("show");
		System.out.println(getMeth);
		
		System.out.println("----------//方法调用------------");
		//要有对象吧
		SOB sob = (SOB)cls.newInstance();
		//invoke方法来调用方法
		getMeth.invoke(sob);
		
		
	}
}

执行结果如下:

%e6%88%aa%e5%9b%be041

工厂模式的例子,嘻嘻

本文链接:http://blog.sunofbeaches.com/archives/141 转载请注明出处.
如果喜欢:点此订阅本站
7K
为您推荐
各种观点