反射-我要窥探类中的世界

反射与类操作

思维导图
利用反射可以做出一个对象具备的所有操作行为,最为关键的是这一切的操作都可以基于Object进行。

取得父类信息
在java中任何的程序类一定会有父类,在Class类中就可以通过如下方法来取得父类或者父接口:

取得类的包名称:public Package getPackage()

取得包名:

package reflect;

interface IFFruit{}
interface IMessage{}
class CLS implements IFFruit,IMessage{}
public class Test2 {
	public static void main(String[] args) {
		Class<?> cls = CLS.class;//取得类对象
		System.out.println(cls.getPackage().getName());
	}

}
//+++++++++++++++++++++++++++++++++++++++
reflect
  • 取得父类的Class对象: public native Class< ? super T> getSuperclass();
  • 取得实现的父接口:public Class<?>[] getInterfaces()

取得父类对象

package reflect;

interface IFFruit{}
interface IMessage{}
class CLS implements IFFruit,IMessage{}
public class Test2 {
	public static void main(String[] args) {
		Class<?> cls = CLS.class;//取得class类对象
		//取得package名称
		System.out.println(cls.getPackage().getName());
		//取得父类名称
		System.out.println(cls.getSuperclass().getName());
		//取得实现的父类接口名称
		Class<?> [] iClasses = cls.getInterfaces();
		for (Class<?> class1 : iClasses) {
			System.out.println(class1.getName());
		}
	}
}

//++++++++++++++++++++++++++
reflect
java.lang.Object
reflect.IFFruit
reflect.IMessage

通过反射可以取得类结构上的所有关键信息。

反射调用构造

一个类中可以存在多个构造方法,如果想要取得类中构造的调用,就可以使用Class类中提供的两个方法。

  • 取得指定参数类型的构造:public Constructor getConstructor(Class< ?>… parameterTypes)throws NoSuchMethodException, SecurityException
  • 取得类中的所有构造:public Constructor< ?>[]getConstructors() throws SecurityException

以上两个方法返回值的类型都是java.lang.refl.Constructo类的实例化对象,这个类之中只需要关注一个方法。

取构造:

class Person{
	public Person(){}
	public Person(String name){}
	public Person (String name,int age) {}
}
public class Test2 {
	public static void main(String[] args) throws NoSuchMethodException, SecurityException {
		Class<?> cls = Person.class;//取得类对象
		//取得单个构造
		Constructor<?> cst = cls.getConstructor();
		System.out.println(cst);
		System.out.println("\n+++++++Cutting line ++++++++++++++\n");
		//取得所有构造
		Constructor<?> [] constructors = cls.getConstructors();
		for (Constructor<?> constructor : constructors) {
			System.out.println(constructor);
		}
	}

}

//+++++++++++++++++++++++++++++
public reflect.Person()

+++++++Cutting line ++++++++++++++

public reflect.Person()
public reflect.Person(java.lang.String)
public reflect.Person(java.lang.String,int)

与之对应的getDeclaredConstructor、getDeclaredConstructors可以取得private属性的构造
以上的操作是直接利用了Construct类中的toString()方法取得了构造方法的完整信息(包含方法权限,参数类表),而使用getName()方法,只会返回构造的包名.类名。
在定义简单java类的时候一定要保存一个无参构造。

class Person {
	private String name;
	private int age;
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
	
}
public class Test2{
	public static void main(String[] args) throws InstantiationException, IllegalAccessException {
		Class<?> class1 = Person.class;
		System.out.println(class1.newInstance());
	}
}
//++++++++++++++++++++
java.lang.InstantiationException

Class类通过反射实例化类对象的时候,只能够调用类中的无参构造,若没有无参构造则无法使用Class类调用,只通过明确的构造调用实例化处理。
通过Constructor类实例化对象。

class Person {
	private String name;
	private int age;
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
	
}
public class Test2{
	public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
		Class<?> class1 = Person.class;
//		System.out.println(class1.newInstance());
//取得指定参数类型的构造方法
		Constructor<?> cons = class1.getConstructor(String.class,int.class);
		System.out.println(cons.newInstance("Jan",20));
	}
}
//+++++++++++++++++++++++++++++++++
Person [name=Jan, age=20]

反射调用普通方法
类中普通方法的反射调用在开发中很容易用到,并且可以节省大量的重复编码。在Class类中有两个取得类中普通方法的函数:

  • 取得全部普通方法:public Method[] getMethods() throws SecurityException
  • 取得指定普通方法:public Method getMethod(String name, Class< ?>… parameterTypes)

以上两个方法的类型的java.lang.refl.Method类的对象,在此类中提供一个调用方法支持:

  • 调用:public Object invoke(Object obj, Object… args)throws IllegalAccessException,IllegalArgumentException,InvocationTargetException

取得类中的全部普通方法

class Person {
	private String name;
	private int age;
	public Person () {}
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
	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 Test2{
	public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
		Class<?> class1 = Person.class;
		Method [] methods = class1.getMethods();//取得所有方法
		for (Method method : methods) {
			System.out.println(method);
		}
	}
}

//++++++++++++++++++++++++++++++++++++++++
public java.lang.String reflect.Person.toString()
public java.lang.String reflect.Person.getName()
public void reflect.Person.setName(java.lang.String)
public int reflect.Person.getAge()
public void reflect.Person.setAge(int)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()

getMethod、getMethods会去祖宗十八代那儿找public权限的方法,而getDeclaredMethod、getDeclaredMethod只会在本类中查找,且无关属性。

之前程序编程的简单java类中的getter、setter方法采用的都是明确的对象调用。
现在有了反射机制处理之后,即使你没有明确的Person类型对象(依然需要实例化对象,Object对象描述,所有的普通方法必须在有实例化对象时候才可以进行调用)就可以通过反射调用。
通过反射调用setter、getter方法

class Person {
	private String name;
	private int age;
	public Person () {}
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
	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 Test2{
	public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException, ClassNotFoundException {
	//或取类对象
		Class<?> cls = Class.forName("reflect.Person");
		//实例化对象
		Object obj = cls.newInstance();
		//取得setter方法
		Method set = cls.getMethod("setName", String.class);
		//调用方法,传入参数
		set.invoke(obj, "Jan");
		Method get = cls.getMethod("getName");
		Object result = get.invoke(obj);
		System.out.println(result);
	}
}
//+++++++++++++++++++++++++++++++
Jan

此类操作的好处是:不再局限于某一类的对象,而是可以通过Object类进行所有类的方法调用。

反射调用类中属性
在之前已经实现了类的构造调用、方法调用,除此之外还有类中属性的调用。
前提是,类中的所有属性一定在类对象实例化时候才会进行空间分配,所以此时如果要想调用类的属性,必须保证有实例化对象。通过反射的newInstance()可以直接取得实例化对象(Object类型)。
在Class类中提供有两组取得属性的方法:

  • 取得全部属性(父类):public Field[] getFields() throws SecurityException
  • 取得指定属性(父类):public Field getField(String name) throwsNoSuchFieldException, SecurityException
  • 取得全部属性(本类):public Field[] getDeclaredFields() throws SecurityException
  • 取得指定属性(本类):public Method getDeclaredMethod(String name, Class< ?>… parameterTypes) throws NoSuchMethodException, SecurityException

取得类中全部属性:

class Person{
	public String name;
	public int age;
}
class Student extends Person{
	private String school;
	public int ID;
}
public class Test2{
	public static void main(String[] args) throws ClassNotFoundException {
		Class<?> cls = Class.forName("reflect.Student");
		//代码块1,类中全部属性(父类 public)
		{
			Field [] fields = cls.getFields();
			for (Field field : fields) {
				System.out.println(field);
			}
		}
		System.out.println("+++++++++++++Cutting line+++++++++++");
		//代码块2,类中全部属性(本类,private所有权限)
		{
			Field [] fields = cls.getDeclaredFields();
			for (Field field : fields) {
				System.out.println(field);
			}
		}
	}
}


//++++++++++++++++++++++++++++++++++

public int reflect.Student.Id
public java.lang.String reflect.Person.name
public int reflect.Person.age
+++++++++++++Cutting line+++++++++++
private java.lang.String reflect.Student.school
public int reflect.Student.Id

在实际开发中,属性基本上都会进行封装处理,所以没有必要去关注父类中的属性,也就是说一般只取本类中的属性为主。
还有就是Field类属性中两个比较核心的方法:

  • . 设置属性内容 : public void set(Object obj,Objectvalue)throws IllegalArgumentException,IllegalAccessException
  • 取得属性内容 : public Object get(Object obj) throws IllegalArgumentException,IllegalAccessException

类中属性操作

class Person{
	private String name;
}
public class Test2 {
	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException {
		//取得类对象
		Class<?> cls = Class.forName("reflect.Person");
		//实例化类对象
		Object obj = cls.newInstance();
		//取得属性
		Field field = cls.getDeclaredField("name");
		//取消封装性(本次JVM中只执行一次)
		field.setAccessible(true);
		//设置属性
		field.set(obj, "Jan");
		//获取
		System.out.println(field.get(obj));
	}
}

//++++++++++++++++++++++++++++++++++
Jan

取得属性类型:

  • public Class< ?> getType()
class Person {
	private String name;
}
public class Test2{
	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException {
		Class<?> cls = Class.forName("reflect.Person");
		Field field = cls.getDeclaredField("name");
		System.out.println(field.getType().getName());
		System.out.println(field.getType().getSimpleName());
	}
}
//+++++++++++++++++++++++++++++++++++++++
java.lang.String
String

将Field取得属性与Method取得方法配合可以进行许多灵活的操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值