百味皆苦 java后端开发攻城狮

java基础复习十四:反射

2019-03-14
百味皆苦

获取类对象

类对象

  • garen和teemo都是Hero对象,他们的区别在于,各自有不同的名称,血量,伤害值
  • Hero和Item都是类,他们的区别在于有不同的方法,不同的属性
  • 类对象,就是用于描述这种类,都有什么属性,什么方法的

获取

  • 在一个JVM中,一种类,只会有一个类对象存在。
  • 准确的讲是一个ClassLoader下,一种类,只会有一个类对象存在。通常一个JVM下,只会有一个ClassLoader。
package reflection;

import charactor.Hero;

public class TestReflection {

	public static void main(String[] args) {
			String className = "charactor.Hero";
			try {
                //方式一
				Class pClass1=Class.forName(className);
                //方式二
				Class pClass2=Hero.class;
                //方式三
				Class pClass3=new Hero().getClass();
				System.out.println(pClass1==pClass2);
				System.out.println(pClass1==pClass3);
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	}
}

  • 无论什么途径获取类对象,都会导致静态属性被初始化,而且只会执行一次。(除了直接使用 Class c = Hero.class 这种方式,这种方式不会导致静态属性被初始化)
package charactor;

public class Hero {
	public String name;
	public float hp;
	public int damage;
	public int id;

	static String copyright;

	static {
		System.out.println("初始化 copyright");
		copyright = "版权由Riot Games公司所有";
	}

}

创建对象

  • 与传统的通过new 来获取对象的方式不同
  • 反射机制,会先拿到Hero的“类对象”,然后通过类对象获取“构造器对象”
  • 再通过构造器对象创建一个对象
package reflection;
import java.lang.reflect.Constructor;
import charactor.Hero;
public class TestReflection {
 
    public static void main(String[] args) {
    	//传统的使用new的方式创建对象
        Hero h1 =new Hero();
        h1.name = "teemo";
        System.out.println(h1);
         
        try {
        	//使用反射的方式创建对象
            String className = "charactor.Hero";
            //类对象
            Class pClass=Class.forName(className);
            //构造器
            Constructor c= pClass.getConstructor();
            //通过构造器实例化
            Hero h2= (Hero) c.newInstance();
            h2.name="gareen";
            System.out.println(h2);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

访问属性

  • 为了访问属性,把name修改为public。

  • 对于private修饰的成员,需要使用setAccessible(true)才能访问和修改。

package charactor;

public class Hero {
	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 Hero(){
		
	}
	public Hero(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(Hero h2) {
		System.out.println(this.name+ " 正在攻击 " + h2.getName());
	}

}

package reflection;

import java.lang.reflect.Field;

import charactor.Hero;
 
public class TestReflection {
 
    public static void main(String[] args) {
            Hero h =new Hero();
            //使用传统方式修改name的值为garen
            h.name = "garen";
            try {
            	//获取类Hero的名字叫做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 只能获取public的,包括从父类继承来的字段。
  • getDeclaredField 可以获取本类所有的字段,包括private的,但是不能获取继承来的字段。 (: 这里只能获取到private的字段,但并不能访问该private字段的,除非加上setAccessible(true))

访问私有属性

  • 可以通过类对象的getDeclaredField()方法获取字段(Field)对象,然后再通过字段对象的setAccessible(true)将其设置为可以访问,接下来就可以通过get/set方法来获取/设置字段的值了。
package chimomo.learning.java.code.reflection;
 
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
 
/**
 * @author Created by Chimomo
 */
public class ReflectionUtil {
    private ReflectionUtil() {
        throw new AssertionError();
    }
 
    /**
     * 通过反射取对象指定字段(属性)的值
     *
     * @param target    目标对象
     * @param fieldName 字段的名字
     * @return 字段的值
     * @throws 如果取不到对象指定字段的值则抛出异常
     */
    public static Object getValue(Object target, String fieldName) {
        Class<?> clazz = target.getClass();
        String[] fs = fieldName.split("\\.");
 
        try {
            for (int i = 0; i < fs.length - 1; i++) {
                Field f = clazz.getDeclaredField(fs[i]);
                f.setAccessible(true);
                target = f.get(target);
                clazz = target.getClass();
            }
 
            Field f = clazz.getDeclaredField(fs[fs.length - 1]);
            f.setAccessible(true);
            return f.get(target);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
 
    /**
     * 通过反射给对象的指定字段赋值
     *
     * @param target    目标对象
     * @param fieldName 字段的名称
     * @param value     值
     */
    public static void setValue(Object target, String fieldName, Object value) {
        Class<?> clazz = target.getClass();
        String[] fs = fieldName.split("\\.");
        try {
            for (int i = 0; i < fs.length - 1; i++) {
                Field f = clazz.getDeclaredField(fs[i]);
                f.setAccessible(true);
                Object val = f.get(target);
                if (val == null) {
                    Constructor<?> c = f.getType().getDeclaredConstructor();
                    c.setAccessible(true);
                    val = c.newInstance();
                    f.set(target, val);
                }
                target = val;
                clazz = target.getClass();
            }
 
            Field f = clazz.getDeclaredField(fs[fs.length - 1]);
            f.setAccessible(true);
            f.set(target, value);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

调用方法

package charactor;

public class Hero {
	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 Hero(){
		
	}
	public Hero(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(Hero h2) {
		// TODO Auto-generated method stub
		
	}

}
package reflection;

import java.lang.reflect.Method;

import charactor.Hero;

public class TestReflection {

	public static void main(String[] args) {
		Hero h = new Hero();

		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();
		}

	}
}

详细文章

通俗易懂的讲反射

面试题

实现过程和作用

  • JAVA语言编译之后会生成一个.class文件,反射就是通过字节码文件找到某一个类、类中的方法以及属性等
  • 反射的实现主要借助以下四个类:Class:类的对象,Constructor:类的构造方法,Field:类中的属性对象,Method:类中的方法对象。
  • 作用:反射机制指的是程序在运行时能够获取自身的信息。在JAVA中,只要给定类的名字,那么就可以通过反射机制来获取类的所有信息。

下一篇 java复习JDBC

Comments

Content