1、认识反射机制
(1)概念
- 在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。
(2)功能
- 在运行时判断任意一个对象所属的类。
- 在运行时构造任意一个类的对象。
- 在运行时判断任意一个类所具有的成员变量和方法。
- 在运行时调用任意一个对象的方法。
- 生成动态代理。
(3)Class类对象的三种实例化模式
- 通过类名获取: 类名.class
Class classa = null;
//1.通过类名
classa = Person.class;
- 通过对象获取:对象名.getClass()
Class classa = null;
//2.通过对象名
//不知道对象类型的时候使用
Person person = new Person();
classa = person.getClass();
//如果传进来是一个Object类,这种做法就是应该的
Object obj = new Person();
classa = obj.getClass();
- 通过全类名获取:Class.forName(全类名)
Class classa = null;
//3.通过全类名(会抛出异常)
String className=" com.atguigu.java.fanshe.Person";
classa = Class.forName(className);
2、Class类的常用方法
-
getName():获得类的完整名字。 getFields():获得类的public类型的属性。
-
getDeclaredFields():获得类的所有属性。
-
getMethods():获得类的public类型的方法。
-
getDeclaredMethods():获得类的所有方法。
-
getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes参数指定方法的参数类型。
-
getConstrutors():获得类的public类型的构造方法。
-
getConstrutor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes参数指定构造方法的参数类型。
-
newInstance():通过类的不带参数的构造方法创建这个类的一个对象。
示例1:方法-Method
public class ReflectionTest {
@Test
public void testMethod() throws Exception{
Class clazz = Class.forName("com.atguigu.java.fanshe.Person");
// //1.获取方法 // 1.1 获取取clazz对应类中的所有方法--方法数组(一)
// 不能获取private方法,且获取从父类继承来的所有方法
Method[] methods = clazz.getMethods();
for(Method method:methods){
System.out.print(" "+method.getName());
}
System.out.println();
//
// 1.2.获取所有方法,包括私有方法 --方法数组(二)
// 所有声明的方法,都可以获取到,且只获取当前类的方法
methods = clazz.getDeclaredMethods();
for(Method method:methods){
System.out.print(" "+method.getName());
}
System.out.println();
//
// 1.3.获取指定的方法
// 需要参数名称和参数列表,无参则不需要写
// 对于方法public void setName(String name) { }
Method method = clazz.getDeclaredMethod("setName", String.class);
System.out.println(method);
// 而对于方法public void setAge(int age) { }
method = clazz.getDeclaredMethod("setAge", Integer.class);
System.out.println(method);
// 这样写是获取不到的,如果方法的参数类型是int型
// 如果方法用于反射,那么要么int类型写成Integer: public void setAge(Integer age) { } // 要么获取方法的参数写成int.class
//
//2.执行方法
// invoke第一个参数表示执行哪个对象的方法,剩下的参数是执行方法时需要传入的参数
Object obje = clazz.newInstance();
method.invoke(obje,2);
//如果一个方法是私有方法,第三步是可以获取到的,但是这一步却不能执行 //私有方法的执行,必须在调用invoke之前加上一句method.setAccessible(true); }
}
示例2:字段-Field
@Test
public void testField() throws Exception{
String className = "com.atguigu.java.fanshe.Person";
Class clazz = Class.forName(className);
//1.获取字段
// 1.1 获取所有字段 -- 字段数组
// 可以获取公用和私有的所有字段,但不能获取父类字段
Field[] fields = clazz.getDeclaredFields();
for(Field field: fields){
System.out.print(" "+ field.getName());
}
System.out.println();
// 1.2获取指定字段
Field field = clazz.getDeclaredField("name");
System.out.println(field.getName());
Person person = new Person("ABC",12);
//2.使用字段
// 2.1获取指定对象的指定字段的值
Object val = field.get(person);
System.out.println(val);
// 2.2设置指定对象的指定对象Field值
field.set(person, "DEF");
System.out.println(person.getName());
// 2.3如果字段是私有的,不管是读值还是写值,都必须先调用setAccessible(true)方法
// 比如Person类中,字段name字段是公用的,age是私有的
field = clazz.getDeclaredField("age");
field.setAccessible(true);
System.out.println(field.get(person));
}
示例3:构造器-Constructor
@Test
public void testConstructor() throws Exception{
String className = "com.atguigu.java.fanshe.Person";
Class<Person> clazz = (Class<Person>) Class.forName(className);
//1. 获取 Constructor 对象
// 1.1 获取全部
Constructor<Person> [] constructors =
(Constructor<Person>[]) Class.forName(className).getConstructors();
for(Constructor<Person> constructor: constructors){
System.out.println(constructor);
}
// 1.2获取某一个,需要参数列表
Constructor<Person> constructor = clazz.getConstructor(String.class, int.class);
System.out.println(constructor);
//2. 调用构造器的 newInstance() 方法创建对象
Object obj = constructor.newInstance("zhagn", 1);
}
3、ClassLoader类加载器
(1)加载器简介
- Class文件由类装载器装载后,在JVM中将形成描述Class结构信息对象,通过该对象可以获知Class的结构信息:如构造函数,属性和方法等。
- 虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。
(2)class.forName()和classLoader区别。
-
class.forName()将类的.class文件加载到jvm中,还会对类进行解释,执行类中static块,还会执行给静态变量赋值的静态方法。Class.forName(name, initialize, loader)带参函数也可控制是否加载static块
-
classLoader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。
4、 反射与Annotation
(1)反射取得Annotation信息
- 获取全部定义的Annotation方法:public Annotation[] getAnnotations()
- 获取指定的Annotation对象:public T getDeclaredAnnotation(Class annotationClass)
import java.lang.annotation.Annotation;
@FunctionalInterface
@Deprecated
interface IMessage { //存在两个Annotation注解
public void send();
}
@SuppressWarnings("serial")
class MessageImpl implements IMessage {
@Override
public void send() {
System.out.println("消息发送...");
}
}
public class AnnotationDemo {
public static void main(String[] args) {
//获取接口上的Annotation信息
Annotation[] annotations = IMessage.class.getAnnotations(); //获取接口上的所有的Annotation
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
}
}
运行结果:
@java.lang.FunctionalInterface()
@java.lang.Deprecated()
并没有获取到注解@SuppressWarnings(“serial”),其原因是该注解无法在程序执行的时候获取。不同的Annotation有他的存在的范围:
- @Retention(RetentionPolicy.RUNTIME) //描述的是运行是生效
- @Retention(RetentionPolicy.SOURCE) //描述的是在源代码时生效
- @Retention(RetentionPolicy.CLASS) //指的是在类定义的时候生效
可以发现"@FunctionalInterface"是在程序运行时生效的Annotation,所以当程序执行的时候可以获取此Annotation。
(2)自定义Annotation
在java中提供有新的语法使用@interface
来定义Annotation。
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@Retention(RetentionPolicy.RUNTIME)
//定义Annotation运行时的策略
@interface DefaultAnnotation { //自定义的Annotation,
public String title(); //获取数据
public String url() default "获取数据的默认值"; //获取数据,默认值
}
class Message {
@DefaultAnnotation(title = "sendMessage") //title不具备默认值,因此必须去显示的定义
public void send(String msg) {
System.out.println("[消息发送]" + msg); //
}
}
public class MyAnnotationDemo {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Method method = Message.class.getDeclaredMethod("send", String.class); //获取指定的方法
DefaultAnnotation annotation = method.getAnnotation(DefaultAnnotation.class);//获取指定的Annotation1
String title = annotation.title(); //直接调用Annotation中的方法
String url = annotation.url();
System.out.println(title + " " + url); //sendMessage 获取数据的默认值
String msg = annotation.title() + " " + url;
method.invoke(Message.class.getDeclaredConstructor().newInstance(),msg); //利用反射实现消息的发送
}
}
(3)工厂设计模式与Annotation整合
工厂设计模式
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface IMessage {
public void send(String msg);
}
class MessageImpl implements IMessage {
@Override
public void send(String msg) {
System.out.println("[消息发送]" + msg);
}
}
class MessageProxy implements InvocationHandler {
private Object target;
public Object bind(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
public boolean connect() {
System.out.println("[代理操作]进行消息发送通道的连接");
return true;
}
public void close() {
System.out.println("[代理操作]关闭连接通道.");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (this.connect()) {
return method.invoke(this.target, args);
} else {
throw new Exception("[error]消息无法进行发送");
}
} finally {
this.close();
}
}
}
class Factory {
private Factory() {
}
public static <T> T getInstance(Class<T> tClass) { //直接返回一个实例化的操作对象
try {
return (T) new MessageProxy().bind(tClass.getDeclaredConstructor().newInstance());
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
class MessageService{
private IMessage message;
public MessageService() {
this.message = Factory.getInstance(MessageImpl.class);
}
public void send(String msg){
this.message.send(msg);
}
}
public class AnnotationFactoryDemo {
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
MessageService service = new MessageService();
service.send("hello");
}
}
每次都需要在MessageService类中给出MessageImpl.class的声明,使用注解形式来简化代码。
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author : S K Y
* @version :0.0.1
*/
interface IMessage {
public void send(String msg);
}
class MessageImpl implements IMessage {
@Override
public void send(String msg) {
System.out.println("[消息发送]" + msg);
}
}
class MessageProxy implements InvocationHandler {
private Object target;
public Object bind(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
public boolean connect() {
System.out.println("[代理操作]进行消息发送通道的连接");
return true;
}
public void close() {
System.out.println("[代理操作]关闭连接通道.");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (this.connect()) {
return method.invoke(this.target, args);
} else {
throw new Exception("[error]消息无法进行发送");
}
} finally {
this.close();
}
}
}
class Factory {
private Factory() {
}
public static <T> T getInstance(Class<T> tClass) { //直接返回一个实例化的操作对象
try {
return (T) new MessageProxy().bind(tClass.getDeclaredConstructor().newInstance());
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
@Retention(RetentionPolicy.RUNTIME)
@interface UseMessage{
public Class<?> thisClass();
}
@UseMessage(thisClass = MessageImpl.class)
class MessageService{
private IMessage message;
public MessageService() {
UseMessage annotation = MessageService.class.getAnnotation(UseMessage.class);
this.message = (IMessage) Factory.getInstance(annotation.thisClass());
}
public void send(String msg){
this.message.send(msg);
}
}
public class AnnotationFactoryDemo {
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
MessageService service = new MessageService();
service.send("hello");
}
}
依据注解来修改整个程序的功能实现,对于面向接口的配置处理可以直接利用Annotation的属性完成控制,从而使得整体代码变得简洁。
执行结果:
[代理操作]进行消息发送通道的连接
[消息发送]hello
[代理操作]关闭连接通道.