00 22Java高级之反射与类操作

在反射机制的处理过程之中不仅仅只是一个实例化对象的处理操作,更多的情况下还有类的组成结构操作,任何一个类的基本组成结构:父类(父接口)、包、属性、方法(构造方法、普通方法)。

1 反射获取类结构信息

一个类的基本信息主要包括类所在的包名称、父类的定义、都接口的定义。
范例:定义一个程序类

package org.lks.abs;

public abstract class AbstractPerson {

}

package org.lks.service;

public interface IMessage {
	void send();
}

package org.lks.service;

public interface IChannel {
	boolean connect();
}

package org.lks.model;

import org.lks.abs.AbstractPerson;
import org.lks.service.IChannel;
import org.lks.service.IMessage;

public class Person extends AbstractPerson implements IMessage, IChannel{

	public Person() {}
	
	@Override
	public boolean connect() {
		return true;
	}

	@Override
	public void send() {
		if(this.connect()) {
			System.out.println("[person] sending ...");
		}
	}

}

如果此时要获得类的一些基础信息则可以通过Class类中的如下方法:
(1)获取包名称:public Package getPackage()
(2)获取继承父类:public Class<? super T> getSuperclass()
(3)获取实现父接口:public Class<?>[] getInterfaces()
范例:获得包名称

package org.lks.demo;

import org.lks.model.Person;

public class JavaAPIDemo {
	public static void main(String[] args) {
		Class<?> cls = Person.class;
		Package packageA = cls.getPackage();
		System.out.println(packageA.getName());
	}
}

范例:获取继承父类

package org.lks.demo;

import org.lks.model.Person;

public class JavaAPIDemo {
	public static void main(String[] args) {
		Class<?> cls = Person.class;
		Class<?> parent = cls.getSuperclass();
		System.out.println(parent.getName());
		System.out.println(parent.getSuperclass().getName());
		System.out.println(parent.getSuperclass().getSuperclass());
	}
}
/*
org.lks.abs.AbstractPerson
java.lang.Object
null
*/

范例:获取父接口

package org.lks.demo;

import org.lks.model.Person;

public class JavaAPIDemo {
	public static void main(String[] args) {
		Class<?> cls = Person.class;
		Class<?>[] parentInterfaces = cls.getInterfaces();
		for(Class<?> parentInterface : parentInterfaces) {
			System.out.println(parentInterface.getName());
		}
	}
}

/*
org.lks.service.IMessage
org.lks.service.IChannel
*/

当获取了一个类的Class对象之后就意味着这个对象可以获取类之中的一切继承结构信息。

2 反射调用构造方法

在一个类之中除了有继承的关系之外最为重要的操作就是类中的结构处理了,而类中的结构里面首先需要观察的就是构造方法的使用问题,实际上之前通过反射实例化对象的时候就已经接触到了构造方法的问题了:
(1)实例化方法替代:clazz.getDeclaredConstructor().newInstance()
所有类的构造方法的获取都可以直接通过Class类来完成,该类中定义有如下的几个方法:
(1)获取本类所有构造方法:public Constructor<?>[] getDeclaredConstructors() throws SecurityException
(2)获取本类指定构造方法:public Constructor<T> getDeclaredConstructor​(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
(3)获取所有构造方法:public Constructor<?>[] getConstructors() throws SecurityException
(4)获取指定构造方法:public Constructor<T> getConstructor​(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
范例:修改Person类的定义

public class Person extends AbstractPerson implements IMessage, IChannel{

	public Person() {}
	
	public Person(String name, int age) {
		
	}
	
	@Override
	public String toString() {
		return "Person [ " + "]";
	}
}

package org.lks.abs;

public abstract class AbstractPerson {
	public AbstractPerson() {}
	
	public AbstractPerson(String name, int age) {}
	

}

范例:获取构造

package org.lks.demo;

import java.lang.reflect.Constructor;

import org.lks.model.Person;

public class JavaAPIDemo {
	public static void main(String[] args) {
		Class<?> cls = Person.class;
		try {
			Constructor<?>[] constructors = cls.getDeclaredConstructors();
			for(Constructor<?> constructor : constructors) {
				System.out.println(constructor);
			}
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

此时获取的是类中的全部构造方法,但是也可以获取一个指定参数的构造,例如:现在的Person类之中提供有两个构造方法:

public class Person extends AbstractPerson implements IMessage, IChannel{

	private String name;
	private int age;
	
	public Person() {}
	
	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}
	
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
}

此时程序打算调用Person类之中的有参构造方法进行Person类对象的实例化处理,这个时候就必须指明要调用的构造,而后通过Constructor类之中提供的实例化方法操作:

范例:调用指定构造实例化对象

package org.lks.demo;

import java.lang.reflect.Constructor;

import org.lks.model.Person;

public class JavaAPIDemo {
	public static void main(String[] args) {
		Class<?> cls = Person.class;
		try {
			Constructor<?> constructor = cls.getDeclaredConstructor(String.class, int.class);
			Object obj = constructor.newInstance("lks", 23);
			System.out.println(obj);
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
}

虽然程序代码本身允许开发者代用有参构造处理,但是如果从实际的开发来讲,所有的使用反射的类中最好提供有无参构造,因为这样的实例化可以达到统一性。

3 反射调用普通方法

在进行反射处理的时候也可以通过反射来获取类中的全部方法,但是需要提醒的是,如果想要通过反射调用这些方法必须有一个前提条件:类之中要提供有实例化对象。

在Class类里面提供有如下的操作可以获取方法对象:
(1)获取全部方法:public Method[] getMethods() throws SecurityException
(2)获取指定方法:public Method getMethod​(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
(3)获取本类全部方法:public Method getDeclaredMethod​(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
(4)获取本类指定方法:public Method[] getDeclaredMethods() throws SecurityException

范例:获取全部方法

package org.lks.demo;

import java.lang.reflect.Method;

import org.lks.model.Person;

public class JavaAPIDemo {
	public static void main(String[] args) {
		Class<?> cls = Person.class;
		Method[] methods = cls.getMethods();
		for(int i = 0; i < methods.length; i++) {
			System.out.println(methods[i]);
		}
	}
}

但是需要注意的是,这个时候的方法信息的获取是依靠Method类提供的toString()方法完成的,很多时候也可由用户自己来拼凑方法信息的显示。

package org.lks.demo;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;

import org.lks.model.Person;

public class JavaAPIDemo {
	public static void main(String[] args) {
		Class<?> cls = Person.class;
		Method[] methods = cls.getMethods();
		for(int i = 0; i < methods.length; i++) {
			int modifier  = methods[i].getModifiers();  //Method's modifier's code
			System.out.print(Modifier.toString(modifier) + " ");  //Method's modifier
			System.out.print(methods[i].getReturnType().getName() + " ");
			System.out.print(methods[i].getName() + "(");  // Method's name
			Parameter[] parameters = methods[i].getParameters();
			for(int i1 = 0; i1 < parameters.length; i1++) {
				System.out.print(parameters[i1].getType().getName() + " " + parameters[i1].getName());
				if(i1 < parameters.length -1) {
					System.out.print(",");
				}
			}
			System.out.print(")");  //next line
			Class<?>[] exceptions = methods[i].getExceptionTypes();
			if(exceptions.length > 0) {
				System.out.print(" throws ");
				for(int i2 = 0; i2 < exceptions.length; i2++) {
					System.out.print(exceptions[i2].getName());
					if(i2 < exceptions.length - 1) {
						System.out.print(",");
					}
				}
				
			}
			System.out.println();
		}
	}
}

这种代码只需要清楚可以根据反射获取方法的结构即可,不需要做过多的深入了解,但是在Method类里面有一个致命的重要方法:public Object invoke​(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException

在Person类里面为name属性追加有setter、getter方法。

public String getName() {
		return name;
	}

public void setName(String name) {
	this.name = name;
}

随后需要通过反射机制来实现Person类之中的setter与getter方法的调用处理。
范例:在不导入指定类开发包的情况下实现属性的配置

package org.lks.demo;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class JavaAPIDemo {
	public static void main(String[] args) {
		Class<?> cls = null;
		try {
			 cls = Class.forName("org.lks.model.Person");
			 Constructor<?> constructor = cls.getDeclaredConstructor();
			 Object obj = constructor.newInstance();
			 String setName = "setName";
			 Method setNameMethod = cls.getMethod(setName, String.class);
			 setNameMethod.invoke(obj, "lks loves hhy");
			 String getName = "getName";
			 Method getNameMethod = cls.getMethod(getName);
			 System.out.println(getNameMethod.invoke(obj));
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
	}
}

利用此类操作整体的形式上不会有任何的明确的类对象产生,一切都是依靠反射机制处理的,这样的处理避免了与某一个类耦合问题。

4 反射调用成员

类结构之中最后一个核心的组成就是成员(Field),大部分情况下都会将其称为成员属性,对于成员信息的获取也是通过Class类完成的,在这个类中提供有如下两组操作方法:
(1)获取本类全部成员:public Field[] getDeclaredFields() throws SecurityException
(2)获取本类指定成员:public Field getDeclaredField​(String name) throws NoSuchFieldException, SecurityException
(3)获取父类全部成员:public Field[] getFields() throws SecurityException
(4)获取父类指定成员:public Field getField​(String name) throws NoSuchFieldException, SecurityException

范例:获取类中成员

package org.lks.demo;

import java.lang.reflect.Field;

import org.lks.model.Person;

public class JavaAPIDemo {
	public static void main(String[] args) {
		Class<?> cls = null;
		cls = Person.class;
		Field[] fields = cls.getFields();
		for(Field field : fields) {
			System.out.println(field.getType());
		}
		Field[] fieldsA = cls.getDeclaredFields();
		for(Field field : fieldsA) {
			System.out.println(field.getType());
		}
	}
}

但是在Field类里面最为重要的操作形式并不是获取全部的成员,而是如下的三个方法:
(1)设置属性内容:public void set​(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException
(2)获取属性内容:public Object get​(Object obj) throws IllegalArgumentException, IllegalAccessException
(3)解除封装:public void setAccessible​(boolean flag)
所有的成员都是在对象实例化之后进行空间分配的,所以此时一定要先有实例化对象之后才可以及进行成员的操作;
范例:直接调用Person类中的name私有成员

package org.lks.demo;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

import org.lks.model.Person;

public class JavaAPIDemo {
	public static void main(String[] args) {
		Class<?> cls = null;
		cls = Person.class;
		try {
			Object obj = cls.getDeclaredConstructor().newInstance();
			Field name = cls.getDeclaredField("name");
			name.setAccessible(true);
			name.set(obj, "lks love hhy.");
			System.out.println(name.get(obj));
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
}

通过一系列的分析可以发现,类之中的构造、方法、成员属性都可以通过反射实现调用,但是对于成员的反射调用很少这样直接处理,大部分操作都应该通过setter或getter处理,所以对于以上的代码只能够说是反射的特色,但是不具备有实际的使用能力,而对于Field类在实际开发之中只有一个方法最为常用:
(1)获取成员类型:public Class<?> getType()

范例:获取Person类中的name成员对象

package org.lks.demo;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

import org.lks.model.Person;

public class JavaAPIDemo {
	public static void main(String[] args) {
		Class<?> cls = null;
		cls = Person.class;
		try {
			Field name = cls.getDeclaredField("name");
			System.out.println(name.getType().getName());
			System.out.println(name.getType().getSimpleName());
		} catch (NoSuchFieldException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
}

在以后开发中进行反射处理的时候,往往会利用Field与Method类实现类中的setter方法调用。
、

5 Unsafe工具类

反射是Java的第一大特点,一旦打开了反射的大门就可以有了更加丰富的类设计形式。除了JVM本身支持的反处理射之外,在Java里面也提供有一个sun.misc.Unsafe类(不安全的操作),这个类的主要特点是可以利用反射来获取对象,并且直接使用底层的C++来代替JVM执行,即:可以绕过JVM的相关的对象的管理机制,如果你一旦使用了Unsafe类,那么你的项目之中将无法继续使用JVM的内存管理机制以及垃圾回收处理。

但是如果想要使用Unsafe类首先需要确认一下这个类之中定义的构造方法与常量问题:
(1)构造方法:private Unsafe() {}
(2)私有常量:private static final Unsafe theUnsafe = new Unsafe();
但是需要注意的是,在这个Unsafe类里面并没有提供static方法,即:不能够通过类似于传统的单例设计模式之中的提供的样式来进行操作,如果想要获得这个类的对象,就必须利用反射机制来完成。

package org.lks.demo;

import java.lang.reflect.Field;

import sun.misc.Unsafe;

public class JavaAPIDemo {
	public static void main(String[] args) {
		Class<?> cls = Unsafe.class;
		try {
			Field unsafe = cls.getDeclaredField("theUnsafe");
			unsafe.setAccessible(true);
			Unsafe uns = (Unsafe) unsafe.get(null);
			System.out.println(uns.getClass().getName());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
}

在传统的开发之中,一个程序类必须要通过实例化对象后才可以调用类中的普通方法,尤其是以单例设计模式为例。

范例:使用Unsafe类绕过实例化对象的管理

package org.lks.demo;

import java.lang.reflect.Field;

import sun.misc.Unsafe;

public class JavaAPIDemo {
	public static void main(String[] args) {
		Class<?> cls = Unsafe.class;
		try {
			Field unsafe = cls.getDeclaredField("theUnsafe");
			unsafe.setAccessible(true);
			Unsafe uns = (Unsafe) unsafe.get(null);
			Singleton single  = (Singleton) uns.allocateInstance(Singleton.class);
			single.print();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
}

class Singleton{
	private Singleton() {}
	
	public void print() {
		System.out.println("hello");
	}
}

Unsafe只能够说为我们的开发提供了一些更加方便的处理机制,但是这种操作由于不受JVM的管理所以如果不是必须的情况下不建议使用,

发布了122 篇原创文章 · 获赞 11 · 访问量 4227

猜你喜欢

转载自blog.csdn.net/weixin_43762330/article/details/104770726
00