反射
反射机制
编译器:将Java代码编译成class文件的过程,该过程只是把代码当作文本进行操作,比如检查错误
运行期:将class文件放入内存中执行
反射机制:在程序运行状态下,对于任意一个类,都能知道这个类的所有属性和方法;对于任意一个对象,都能调用它的任意方法和属性,对于这种动态获取信息以及动态调用对象方法的功能称为Java的反射机制
java.lang.Class类
要知道一个类的属性和方法,必须先获取该类的字节码文件对象。获取类的信息时,使用的就是Class类中的方法,所以先要获取到每一个字节码文件(.class)对应的Class类型的对象,Class类的一个实例表示Java的一种数据类型。Class没有公有的构造方法,Class实例是由JVM在类加载时自动创建的
类型 | 访问方法 | 返回值类型 | 说明 |
---|---|---|---|
包路径 | getPackage() | Package 对象 | 获取该类的存放路径 |
类名称 | getName() | String 对象 | 获取该类的名称 |
继承类 | getSuperclass() | Class 对象 | 获取该类继承的类 |
实现接口 | getlnterfaces() | Class 型数组 | 获取该类实现的所有接口 |
构造方法 | getConstructors() | Constructor 型数组 | 获取所有权限为 public 的构造方法 |
getDeclaredContruectors() | Constructor 对象 | 获取当前对象的所有构造方法 | |
方法 | getMethods() | Methods 型数组 | 获取所有权限为 public 的方法 |
getDeclaredMethods() | Methods 对象 | 获取当前对象的所有方法 | |
成员变量 | getFields() | Field 型数组 | 获取所有权限为 public 的成员变量 |
getDeclareFileds() | Field 对象 | 获取当前对象的所有成员变量 | |
内部类 | getClasses() | Class 型数组 | 获取所有权限为 public 的内部类 |
getDeclaredClasses() | Class 型数组 | 获取所有内部类 | |
内部类的声明类 | getDeclaringClass() | Class 对象 | 如果该类为内部类,则返回它的成员类,否则返回 null |
getFields()
和getMethods()
方法时会依次获取权限为public的字段和变量,然后是从父类中继承的成员变量和方法;getDeclareFields()
和getDeclareMethods()
只是获取在本类中定义的成员变量和方法
java.lang.reflect 包
- Constructor 类:提供类的构造方法信息。
- Field 类:提供类或接口中成员变量信息。
- Method 类:提供类或接口成员方法信息。
- Array 类:提供了动态创建和访问 Java 数组的方法。
- Modifier 类:提供类和成员访问修饰符信息。
1.Constructor
如果访问指定的构造方法,需要根据该构造方法的入口参数的类型来访问
方法名称 | 说明 |
---|---|
isVarArgs() | 查看该构造方法是否允许带可变数量的参数,如果允许,返回 true,否则返回 false |
getParameterTypes() | 按照声明顺序以 Class 数组的形式获取该构造方法各个参数的类型 |
getExceptionTypes() | 以 Class 数组的形式获取该构造方法可能抛出的异常类型 |
newInstance(Object … initargs) | 通过该构造方法利用指定参数创建一个该类型的对象,如果未设置参数则表示 采用默认无参的构造方法 |
setAccessiable(boolean flag) | 如果该构造方法的权限为 private,默认为不允许通过反射利用 netlnstance() 方法创建对象。如果先执行该方法,并将入口参数设置为 true,则允许创建对 象 |
getModifiers() | 获得可以解析出该构造方法所采用修饰符的整数 |
2.Modifier
静态方法名称 | 说明 |
---|---|
isStatic(int mod) | 如果使用 static 修饰符修饰则返回 true,否则返回 false |
isPublic(int mod) | 如果使用 public 修饰符修饰则返回 true,否则返回 false |
isProtected(int mod) | 如果使用 protected 修饰符修饰则返回 true,否则返回 false |
isPrivate(int mod) | 如果使用 private 修饰符修饰则返回 true,否则返回 false |
isFinal(int mod) | 如果使用 final 修饰符修饰则返回 true,否则返回 false |
toString(int mod) | 以字符串形式返回所有修饰符 |
3.Method
静态方法名称 | 说明 |
---|---|
getName() | 获取该方法的名称 |
getParameterType() | 按照声明顺序以 Class 数组的形式返回该方法各个参数的类型 |
getReturnType() | 以 Class 对象的形式获得该方法的返回值类型 |
getExceptionTypes() | 以 Class 数组的形式获得该方法可能抛出的异常类型 |
invoke(Object obj,Object…args) | 利用 args 参数执行指定对象 obj 中的该方法,返回值为 Object 类型 |
isVarArgs() | 查看该方法是否允许带有可变数量的参数,如果允许返回 true,否则返回 false |
getModifiers() | 获得可以解析出该方法所采用修饰符的整数 |
4.Field
方法名称 | 说明 |
---|---|
getName() | 获得该成员变量的名称 |
getType() | 获取表示该成员变量的 Class 对象 |
get(Object obj) | 获得指定对象 obj 中成员变量的值,返回值为 Object 类型 |
set(Object obj, Object value) | 将指定对象 obj 中成员变量的值设置为 value |
getlnt(0bject obj) | 获得指定对象 obj 中成员类型为 int 的成员变量的值 |
setlnt(0bject obj, int i) | 将指定对象 obj 中成员变量的值设置为 i |
setFloat(Object obj, float f) | 将指定对象 obj 中成员变量的值设置为 f |
getBoolean(Object obj) | 获得指定对象 obj 中成员类型为 boolean 的成员变量的值 |
setBoolean(Object obj, boolean b) | 将指定对象 obj 中成员变量的值设置为 b |
getFloat(Object obj) | 获得指定对象 obj 中成员类型为 float 的成员变量的值 |
setAccessible(boolean flag) | 此方法可以设置是否忽略权限直接访问 private 等私有权限的成员变量 |
getModifiers() | 获得可以解析出该方法所采用修饰符的整数 |
package com.henrik.reflect;
import java.io.Serializable;
import java.util.Date;
public interface ReflectService {
public String response(String info);
public Date getTime();
}
class ReflectServiceImpl implements ReflectService{
@Override
public String response(String info) {
return "response"+info;
}
@Override
public Date getTime() {
return new Date();
}
}
class ClientCall implements Serializable{
//版本号:在反序列化期间用于验证序列化对象的发送方和接收方是否为该对象加载了与序列化兼容的类。
private static final long serialVersionUID = 8316370391638512860L;
private String className; //表示类名或接口名
private String methodName; //表示方法
private Class<?>[] paramTypes; //表示方法参数类型
private Object[] params; //表示方法参数值
private Object result; //方法的执行结果
public ClientCall() {}
public ClientCall(String className,String methodName,Class<?>[] paramTypes,Object[] params) {
this.className = className;
this.methodName = methodName;
this.paramTypes = paramTypes;
this.params = params;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public Class<?>[] getParamTypes() {
return paramTypes;
}
public void setParamTypes(Class<?>[] paramTypes) {
this.paramTypes = paramTypes;
}
public Object[] getParams() {
return params;
}
public void setParams(Object[] params) {
this.params = params;
}
public Object getResult() {
return result;
}
public void setResult(Object result) {
this.result = result;
}
@Override
public String toString() {
return "ClientCall [className=" + className + ", methodName=" + methodName + ", paramTypes=" + paramTypes + ", params=" +params +"]";
}
}
package com.henrik.reflect;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
public class ReflectServer {
@SuppressWarnings("resource")
private void service() throws Exception {
ServerSocket serverSocket = new ServerSocket(8090);
while(true) {
Socket socket = serverSocket.accept();
InputStream in = socket.getInputStream();
ObjectInputStream ois = new ObjectInputStream(in);
OutputStream out = socket.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(out);
ClientCall call = (ClientCall)ois.readObject();
System.out.println(call);
call = invoke(call);
oos.writeObject(call);
ois.close();
oos.close();
socket.close();
}
}
@SuppressWarnings("deprecation")
private ClientCall invoke(ClientCall call) {
Object result = null;
try {
String className = call.getClassName();
String methodName = call.getMethodName();
Class<?>[] paramTypes = call.getParamTypes();
Object[] params = call.getParams();
Class<?> classType = Class.forName(className);
Method method = classType.getMethod(methodName, paramTypes);
ReflectService remoteObject = (ReflectService)classType.newInstance();
if(remoteObject == null) {
throw new Exception(className+"的远程对象不存在");
}else {
result = method.invoke(remoteObject, params);
}
} catch (Exception e) {
result = e;
}
call.setResult(result);
return call;
}
public static void main(String[] args) throws Exception {
ReflectServer server = new ReflectServer();
server.service();
}
}
package com.henrik.reflect;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
public class ReflectClient {
public void invoke() throws Exception{
Socket socket = new Socket("localhost",8090);
OutputStream out = socket.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(out);
InputStream in = socket.getInputStream();
ObjectInputStream ois = new ObjectInputStream(in);
//创建一个远程调用对象
ClientCall call = new ClientCall("com.henrik.reflect.ReflectServiceImpl","response",new Class[] {String.class},new Object[] {"Java"});
oos.writeObject(call);//向服务器发送ClientCall对象
call = (ClientCall)ois.readObject();
System.out.println(call.getResult());
oos.close();
ois.close();
socket.close();
}
public static void main(String[] args) throws Exception {
new ReflectClient().invoke();
}
}
注解
注解可以看作代码中的补充信息,是一种描述数据的数据,它本身不会改变程序的运行结果,也不会影响程序运行的性能,注解本质是一种数据类型,是一种接口类型。
使用场景
- 文档注释
/** */
中使用,是Java最早提供的注解,比如@see
,@param
,@return
- 跟踪代码依赖性,实现替代配置文件功能,现在的框架基本都使用注解方式减少配置文件的数量,比如Spring框架
- 编译时进行格式检查,比如
@Override
,如果有该注解的方法没有重写父类方法,编译时会被检查出
Java内置注解
到Java8为止JavaSE提供了11个内置注解:java.lang
包中5个基本注解, java.lang.annotation
包中6个元注解(自定义注解中会使用,对其它注解进行说明的注解)
1.基本注解
@Override
:指定方法重写,用于注解方法
@Deprecated
:表示某个元素(类,方法等)已过时,用于注解类,接口,成员方法和成员变量等
@SuppressWarnings
:指示该注解修饰的元素及其子元素取消显示指定的编译器警告
关键字 | 用途 |
---|---|
all | 抑制所有警告 |
boxing | 抑制装箱、拆箱操作时候的警告 |
cast | 抑制映射相关的警告 |
dep-ann | 抑制启用注释的警告 |
deprecation | 抑制过期方法警告 |
fallthrough | 抑制在 switch 中缺失 breaks 的警告 |
finally | 抑制 finally 模块没有返回的警告 |
hiding | 抑制相对于隐藏变量的局部变量的警告 |
incomplete-switch | 忽略不完整的 switch 语句 |
nls | 忽略非 nls 格式的字符 |
null | 忽略对 null 的操作 |
rawtypes | 使用 generics 时忽略没有指定相应的类型 |
restriction | 抑制禁止使用劝阻或禁止引用的警告 |
serial | 忽略在 serializable 类中没有声明 serialVersionUID 变量 |
static-access | 抑制不正确的静态访问方式警告 |
synthetic-access | 抑制子类没有按最优方法访问内部类的警告 |
unchecked | 抑制没有进行类型检查操作的警告 |
unqualified-field-access | 抑制没有权限访问的域的警告 |
unused | 抑制没被使用过的代码的警告 |
@SafeVarargs
:指示该注解修饰的元素及其子元素取消显示使用可变参数+泛型参数可能造成的堆污染警告
@FunctionalInterface
:指定接口为函数式接口(一个接口中只有一个抽象方法),用于接口
2.元注解
@Documented
:修饰的注解类会被JavaDoc工具提取为文档
@Target
:描述注解可以使用在什么地方,通过成员变量value(java.lang.annotation.ElementType 枚举类型的数组)进行指定
名称 | 说明 |
---|---|
CONSTRUCTOR | 用于构造方法 |
FIELD | 用于成员变量(包括枚举常量) |
LOCAL_VARIABLE | 用于局部变量 |
METHOD | 用于方法 |
PACKAGE | 用于包 |
PARAMETER | 用于类型参数(JDK 1.8新增) |
TYPE | 用于类、接口(包括注解类型)或 enum 声明 |
Retention
:描述注解的生命周期,通过成员变量value(java.lang.annotation.RetentionPolicy 枚举类型)进行指定
名称 | 说明 |
---|---|
SOURCE | 在源文件中有效(即源文件保留) |
CLASS | 在 class 文件中有效(即 class 保留) |
RUNTIME | 在运行时有效(即运行时保留) |
@Inherited
:描述该注解可以被继承,即父类使用了被@Inherited
修饰的注解,那么子类自动具有该注解
@Repeatable
:描述该注解可以对同一个程序元素重复注解,即被@Repeatable
修饰的注解可以多次在同一个程序元素上使用
@Native
:被@Native
修饰的变量可以被本地代码引用,常常被代码生成工具使用s