首页 > 代码库 > java反射

java反射

1.首先理解什么是反射?

反射(Reflection)能够让运行于JVM中的程序检测和修改运行时的行为。

2.为什么需要反射?

反射能够让我们:

  • 在运行时检测对象的类型;
  • 动态构造某个类的对象;
  • 检测类的属性和方法;
  • 任意调用对象的方法;
  • 修改构造函数、方法、属性的可见性等;
--------------------------------------华丽分割线-----------------------------------------------

1)在面向对象的世界里,万事万物皆对象。(java语言中,静态的成员、普通数据类型等)
   类是不是对象?类是谁的对象?//类是对象,类是java.lang.Class类的实例对象
2)这个对象到底如何表示?
3)Class.forName("类的全称"), 不仅表示了类的类类型,还表示了动态加载类(编译时加载类是静态加载类、运行时加载类是动态加载类)

--------------------------------------华丽分割线-----------------------------------------------

3.Class类实例对象的方法

Class实例对象的三种方法:a.通过类的隐含的静态成员class; b.通过该类的getClass()方法;c.通过类的全称,用Class.forName("类名")获取。eg:

class ClassDemo0{
	public static void main(String[] args) {
		Foo foo1=new Foo();
		//1,任何一个类都有一个隐含的静态成员class
		Class c1=Foo.class;
		//2,已知该类,通过getClass方法
		Class c2=foo1.getClass();

		//注c1,c2表示Foo类的类类型(class type)
		System.out.println(c1==c2);  //true, 一个类只可能是Class类的一个实例对象

		//3,通过类的全称获取
		Class c3=null;
		try{
			c3=Class.forName("Foo");
		}catch(ClassNotFoundException e){
			e.printStackTrace();
		}
		System.out.println(c2==c3);//true

		//通过类的类类型创建该类的对象实例,通过c1,c2,c3创建Foo的对象实例
		try{
			Foo foo=(Foo)c1.newInstance();//前提:该类要有无参构造函数
		}catch(InstantiationException e){
			e.printStackTrace();
		}catch(IllegalAccessException e){
			e.printStackTrace();
		}
	}
}

class Foo{
	void print(){
		System.out.println("Foo");
	}
}
4.动态加载类的实用例子

假如想要写一个类调用类word或Excel,如下程序,如果没有Word和Excel类编译时肯定会出错,若希望有其中一个类存在也可以编译通过,则需要用到动态加载类

class Office{
	public static void main(String[] args) {

		 //new创建对象 是静态加载类,在编译时刻就需要加载所有的可能使用到的类,(若没有建Word类),编译不通过
		//若有建Word类,但Excel类没建,但编译还是不能通过
		//希望用到时再加载,可以通过动态加载类实现
		if("Word".equals(args[0])){
			Word w=new Word();    
			w.start();
		}

		if("Excel".equals(args[0])){
			Excel e=new Excel();
			e.start();
		}
	}
}
运用动态加载类,使程序在运行时再加载,顺利通过编译,这也有利于后期增加功能,实现在线升级,增强扩展性
class OfficeBetter{
	public static void main(String[] args) {
		try{
			//动态加载类,在运行时加载
			Class c=Class.forName(args[0]);
			//通过类类型,创建该类对象
			//Word w=(Word)c.newInstance();    //如果加载的是Excel呢?
			//Excel e=(Excel)c.newInstance();  //如果加载的是Excel呢?

			//后期增加功能就不用改动OfficeBetter,实现在线升级,扩展性更强
			OfficeAble oa=(OfficeAble)c.newInstance();  //所以做一个标准————功能型的类尽量使用动态加载
			oa.start();
		}catch(Exception e){
			e.printStackTrace();
		}
	}
}

interface OfficeAble{
	public void start();
}

class Word implements OfficeAble{
	public void start(){
		System.out.println("word...start...");
	}
}

class Excel implements OfficeAble{
	public void start(){
		System.out.println("Excel...start...");
	}
}
注:功能性的类尽量使用动态加载的方法

5.获取类的信息

a.基本数据类型的类类型

                Class c1=int.class;
		Class c2=String.class;//
		Class c3=double.class;
		Class c4=Double.class;
		Class c5=void.class;

		System.out.println(c1.getName());
		System.out.println(c2.getName());
		System.out.println(c2.getSimpleName());  //只获取类名
		System.out.println(c3.getName());
		System.out.println(c4.getName());
		System.out.println(c5.getName());

		/*output:
		int
		java.lang.String
		String
		double
		java.lang.String
		void
		*/
b.类的成员函数(使用Methods),成员变量(使用Field),构造函数的获取(Constructor)

class ClassUtils{

	/*
	 *打印类的信息,包括类的成员函数
	*/
	public static void printClassMessage(Object obj){
		//要获取类的信息 首先要获取类的类类型
		Class c=obj.getClass();//传递的是哪个子类的对象 c就是该子类的类类型
		//获取类的名称
		System.out.println("类的名称是:"+c.getName());
		/*
		Method类,方法对象
		一个成员方法就是一个Method对象
		getMethods()方法获取的是所有的public的函数,包括父类继承而来的
		getDeclaredMethods()获取的是所有该类自己声明的方法,不问访问父类继承的
		*/
		Methods[] ms=c.getMethods();
		for(int i=0;i<ms.length;i++){
			//得到方法的返回值类型的类类型
			Class returnType=ms[i].getReturnType();
			System.out.print(returnType.getName()+" ");
			//得到方法的名称
			System.out.print(ms[i].getName()+"(");
			//获取参数类型-->得到的是参数列表的类型的类类型
			Class[] paramTypes=ms[i].getParameterTypes();
			for(Class class1:paramTypes){
				System.out.print(class1.getName()+",");
			}
			System.out.println(")");
			
		}

	}
	/*
	*打印类的信息,包括类的成员变量
	*/
	public static void printFieldMessage(Object obj){
		Class c=obj.getClass();
		/*
		成员变量也是对象
		java.lang.reflect.Field
		Field类封装了关于成员变量的操作
		getFields()方法获取的是所有的public的成员变量的信息
		getDeclareFields获取的是该类自己声明的成员变量的信息
		*/
		//Filed[] fs=c.getFileds();
		Field[] fs=c.getDeclareFields();
		for(Filed field:fs){
			//得到成员变量的类型的类类型
			Class fieldType=field.getType();
			String typeName=fieldType.getName();
			//得到成员变量的名称
			String fieldName=field.getName();
			System.out.println(typeName+" "+fieldName);
		}
	}

	/*
	打印对象的构造函数的信息
	*/
	public static void printConMessage(Object obj){
		Class c=obj.getClass();
		/*
		构造函数也是对象
		java.lang.Constructor中封装了构造函数的信息
		getConstructors获取所有的public的构造函数
		getDeclaredConstructors所得的构造函数
		*/
		//Constructor[] cs=c.getContructors();
		Constructor[] cs=c.getDeclaredConstructors();
		for(Constructor constructor:cs){
			System.out.print(constructor.getName()+"(");
			//获取构造函数的参数列表-->得到的是参数列表的类类型
			Class[] paramTypes=constructor.getParameterTypes();
			for(Class class1:paramTypes){
				System.out.print(class1.getName+",");
			}
			System.out.println(")");
		}
	}
}
6.方法的反射

1)如何获取某个方法?
   方法的名称和方法的参数列表才能唯一决定某个方法
2)方法的反射的操作?
   method.invoke(对象,参数列表);

class MethodDemo{
	public static void main(String[] args) {
		//要获取print(int,int)方法 
		//1.要获取一个方法就是获取类的信息,获取类的信息首先要获取类的类类型
		A a1=new A();
		Class c=a1.getClass();
		//2.获取方法名称和参数列表来决定
		try{
			Method m=c.getMethod("print",new Class[]{int.class,int.class});
			//或写成 c.getMethod("print",int.class,int.class);

			//方法的反射操作   如何达到和a1.print(10,20)的效果?
			//方法如果没有返回值返回null,有返回值返回具体的返回值
			Object o=m.invoke(a1,new Object[]{10,20});  //m.invoke(a1,10,20);

			//获取方法print(String,String);
			Method m1=c.getMethod("print",String.class,String.class);
			m.invoke(a1,"hello","world");

			//获取无参方法
			Method m2=c.getMethod("print");// or c.getMethod("print",Object[]{});
			m.invoke(a1);   //m.invoke(a1,new Object[]{});
		}catch(Exception e){
			e.printStackTrace();
		}
	}
}

class A{
	public void print(int a,int b){
		System.out.println(a+b);
	}
	public void print(String a,String b){
		System.out.println(a.toUpperCase()+","+b.toLowerCase());
	}

	public void print(){
		System.out.println("helloworld");
	}
}
最后通过一个栗子来理解集合泛型的本质:集合的泛型是去泛型化,java中集合的泛型,是防止错误输入的,只在编译阶段有效,绕过编译就无效了,所以可以通过方法的反射来操作,绕过编译。

class MethodDemo{
	public static void main(String[] args) {
		ArrayList list=new ArrayList();

		ArrayList<String> list1=new ArrayList<String>();
		list1.add("hello");
		//list1.add(20); //error
		Class c1=list.getClass();
		Class c2=list2.getClass();
		System.out.println(c1==c2);  //true;

		//反射的操作都是编译之后的操作

		/*
		c1==c2结果返回true说明编译之后集合的泛型是去泛型化
		java中集合的泛型,是防止错误输入的,只在编译阶段有效
		绕过编译就无效了
		验证:可以通过方法的反射来操作,绕过编译
		*/
		try{
			Method m=c2.getMethod("add",Object.class);
			m.invoke(list1,20);//绕过编译的操作,就绕过了泛型
			System.out.println(list1.size());
			System.out.println(list1);
		}catch(Exception e){
			e.printStackTrace();
		}
	}
}

---EOF---

java反射