java反射

概念

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

Class

Class不是我们平常声明类时所用的关键字class,它是一个类,它的对象用来描述一个运行状态的类或接口。

反射使用

假设我们现在有一个Hero类

package pojo;
public class Hero {
	public String name; //姓名
    public float hp; //血量
    public float armor; //护甲
    public int moveSpeed; //移动速度
    //---------------构造方法-------------------
	//(默认的构造方法)
	Hero(String str){
		System.out.println("(默认)的构造方法 s = " + str);
	}
	
	//无参构造方法
	public Hero(){
		System.out.println("调用了公有、无参构造方法执行了。。。");
	}
	
	//有一个参数的构造方法
	public Hero(char name){
		System.out.println("姓名:" + name);
	}
	
	//有多个参数的构造方法
	public Hero(String name ,float hp){
		System.out.println("姓名:"+name+"血量:"+ hp);
	}
	
	//受保护的构造方法
	protected Hero(boolean n){
		System.out.println("受保护的构造方法 n = " + n);
	}
	
	//私有构造方法
	private Hero(float hp){
		System.out.println("私有的构造方法   血量:"+ hp);
	}
}

获取类对象

  1. Class.forName()常用
  2. Hero.class
  3. new Hero().getClass()
package pojo;
public class ObjectTest {
	
	public static void main(String[] args) {
        String className = "pogo.Hero";
		try {
        	//获取类对象的第一种方式
            Class pClass1 = Class.forName(className);
            //获取类对象的第二种方式
            Class pClass2 = Hero.class;
            //获取类对象的第三种方式
            Class pClass3 = new Hero().getClass();
            System.out.println(pClass1==pClass2);//输出true
            System.out.println(pClass1==pClass3);//输出true
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
 }
}

常用第一种,第二种需要导入类的包,依赖太强,不导包就抛编译错误。第三种对象都有了还要反射干什么。一般都第一种,一个字符串可以传入也可写在配置文件中等多种方法。

创建对象

与传统的通过new 来获取对象的方式不同反射机制,反射会先拿到Hero的“类对象”,然后通过类对象获取“构造器对象”再通过构造器对象创建一个对象。

1.获取类对象 Class class = Class.forName("pojo.Hero");
2.获取构造器对象 Constructor con = clazz.getConstructor(形参.class);
3 获取对象 Hero hero =con.newInstance(实参);
package test;
public class ConstructorTest {
	
	 
	/*
	 * 通过Class对象可以获取某个类中的:构造方法、成员变量、成员方法;并访问成员;
	 * 
	 * 1.获取构造方法:
	 * 		1).批量的方法:
	 * 			public Constructor[] getConstructors():所有"公有的"构造方法
	            public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
	     
	 * 		2).获取单个的方法,并调用:
	 * 			public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
	 * 			public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
	 * 		
	 * 2.创建对象
	 * 		Constructor对象调用newInstance(Object... initargs)
	 */

	 
	public static void main(String[] args) throws Exception {
		//1.加载Class对象
		Class clazz = Class.forName("pojo.Hero");
		
		
		//2.获取所有公有构造方法
		System.out.println("**********************所有公有构造方法*********************************");
		Constructor[] conArray = clazz.getConstructors();
		for(Constructor c : conArray){
			System.out.println(c);
		}
		
		
		System.out.println("************所有的构造方法(包括:私有、受保护、默认、公有)***************");
		conArray = clazz.getDeclaredConstructors();
		for(Constructor c : conArray){
			System.out.println(c);
		}
		
		System.out.println("*****************获取公有、无参的构造方法*******************************");
		Constructor con = clazz.getConstructor(null);
		//1>、因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的类型,切记是类型
		//2>、返回的是描述这个无参构造函数的类对象。
		System.out.println("con = " + con);
		//调用构造方法
		Object obj = con.newInstance();
		
		
		System.out.println("******************获取私有构造方法,并调用*******************************");
		con = clazz.getDeclaredConstructor(float.class);
		System.out.println(con);
		//调用构造方法
		con.setAccessible(true);//暴力访问(忽略掉访问修饰符)
		obj = con.newInstance(100);
	}
}

输出:
**********************所有公有构造方法*********************************
public pojo.Hero(java.lang.String,float)
public pojo.Hero(char)
public pojo.Hero()
************所有的构造方法(包括:私有、受保护、默认、公有)***************
private pojo.Hero(float)
protected pojo.Hero(boolean)
public pojo.Hero(java.lang.String,float)
public pojo.Hero(char)
public pojo.Hero()
pojo.Hero(java.lang.String)
*****************获取公有、无参的构造方法*******************************
con = public pojo.Hero()
调用了公有、无参构造方法执行了。。。
******************获取私有构造方法,并调用*******************************
private pojo.Hero(float)
私有的构造方法   血量:100.0

获取成员变量并使用

1.获取HeroPlus类的对象 new方法/第2章中的方法 h
2. 获取属性 Field f1 = h.getDeclaredField("属性名")
3. 修改属性 f1.set(h,实参),注意这里的h是对象,不是类对象

新增HeroPlus类

package pojo;
public class HeroPlus {
	public String name;
    public float hp;
    public int damage;
    public int id;
     
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public HeroPlus(){
         
    }
    public HeroPlus(String string) {
        name =string;
    }
 
    @Override
    public String toString() {
        return "Hero [name=" + name + "]";
    }
    public boolean isDead() {
        // TODO Auto-generated method stub
        return false;
    }
    public void attackHero(HeroPlus h2) {
        System.out.println(this.name+ " 正在攻击 " + h2.getName());
    }
    
    public static void main(String[] args) {
		System.out.println("执行main方法");
	}
}

获取属性并修改

package test;
public class ParaTest {
	 public static void main(String[] args) {
         HeroPlus h =new HeroPlus();
         //使用传统方式修改name的值为garen
         h.name = "garen";
        
         try {
             //获取类HeroPlus的名字叫做name的字段
             Field f1= h.getClass().getDeclaredField("name");
             //修改这个字段的值
             f1.set(h, "teemo");
             //打印被修改后的值
             System.out.println(h.name);
              
         } catch (Exception e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }
 }
}

getField和getDeclaredField的区别
getField 只能获取public的,包括从父类继承来的字段。
getDeclaredField 可以获取本类所有的字段,包括private的,但是 不能获取继承来的字段。 (注: 这里只能获取到private的字段,但并不能访问该private字段的值,除非加上setAccessible(true))

获取成员方法并使用

获取HeroPlus类的对象 h
获取成员方法:
public Method getMethod(String name ,Class<?>… parameterTypes):获取"公有方法";(包含了父类的方法也包含Object类)
public Method getDeclaredMethods(String name ,Class<?>… parameterTypes) :获取成员方法,包括私有的(不包括继承的)
参数解释:
  name : 方法名;
  Class … : 形参的Class类型对象
调用方法
Method --> public Object invoke(Object obj,Object… args):
参数说明:
  obj : 要调用方法的对象;
  args:调用方式时所传递的实参;
package test;
public class MethodTest {
	public static void main(String[] args) {
	
	 HeroPlus h = new HeroPlus();
	 
     try {
         // 获取这个名字叫做setName,参数类型是String的方法
         Method m = h.getClass().getMethod("setName", String.class);
         // 对h对象,调用这个方法
         m.invoke(h, "盖伦");
         // 使用传统的方式,调用getName方法
         System.out.println(h.getName());

     } catch (Exception e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
     }
 }
}

获取main方法并使用

package test;
public class MainTest {
	public static void main(String[] args) {
		try {
			//1、获取HeroPlus对象的字节码
			Class clazz = Class.forName("pojo.HeroPlus");
			
			//2、获取main方法,第一个参数:方法名称,第二个参数:方法形参的类型,
			 Method methodMain = clazz.getMethod("main", String[].class);
			//3、调用main方法
			// methodMain.invoke(null, new String[]{"a","b","c"});
			//第一个参数,对象类型,因为方法是static静态的,所以为null可以,第二个参数是String数组,这里要注意在jdk1.4时是数组,jdk1.5之后是可变参数
			//这里拆的时候将  new String[]{"a","b","c"} 拆成3个对象。所以需要将它强转。
			 methodMain.invoke(null, (Object)new String[]{"a","b","c"});//方式一
			// methodMain.invoke(null, new Object[]{new String[]{"a","b","c"}});//方式二
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×