反射是Java程序开发语言的特征之一,它允许运行中的java程序对自身进行检查
,并能直接 *** 作程序的内部属性和方法
优点:
运行期类型的判断,动态加载类,代码灵活度高;
缺点:
性能低,安全性被破坏
常用api
主要通过以下类实现反射
Field
Constructor
Method
Class
Object
获取Class对象
方式一
通过Object类
的 getClass()
获取反射对象
Book book = new Book();
Class bookClass = book.getClass();
System.out.println("完全限定名:" + bookClass.toString());
System.out.println("类的包名:" + bookClass.getPackage()); // 包名
System.out.println("类的包名+类名:" + bookClass.getName());
System.out.println("类名: " + bookClass.getSimpleName());
方式二
通过类名访问class关键字属性
,获取该类型的Class对象
Class bookClass = Book.class;
方式三
通过Class类
的 forName() 静态方法
获取,需传入类的完全限定名
(包名.类名)
Class bookClass = Class.forName("reflect.Book");//包名.类名
通过Class对象调用对应类的构造方法
调用无参构造方法
OObject obj=bookClass.newInstance();//通过反射创建对象
获得有参构造方法并调用
通过Class对象
getDeclaredConstructor
(构造方法参数 . class)
获得构造方法的构造器对象
通过构造器对象 . newInstance(传入构造方法参数)
调用构造方法
对于private私有构造方法
的调用必须使用构造器对象 . setAccessible(true)
把权限设置为true
使权限检查机制关闭
Constructor constuctor = bookClass.getDeclaredConstructor(String.class, String.class);//传入参数类型.class
constuctor.setAccessible(true);//权限检查机制关闭
Object obj = constuctor.newInstance("张碧晨", "华晨宇");//调用有参构造
获得所有参构造方法并打印
把所有构造方法对象放入构造器数组中
Constructor[] constructros = bookClass.getDeclaredConstructors();
获取当前Class类型中成员变量并赋值
获得所有成员变量
Field[] fields = bookClass.getDeclaredFields();
for (Field f : fields) {
System.out.println(f);
System.out.println("访问修饰符:" + f.getModifiers());
System.out.println("类型:" + f.getType());
System.out.println("名称:" + f.getName());
System.out.println();
}
按照成员变量的名称获取成员变量Field对象并赋值
对于private私有成员变量通过
Field对象 . setAccessible(true)
把权限设置为true使权限检查机制关闭
Field descField = bookClass.getDeclaredField("desc");//传入成员变量名字
descField.set(bookClass, "这个是desc成员变量");//传入类对象和赋值的内容
//获取私有成员变量
Field bookNameField = bookClass.getDeclaredField("bookName");//传入成员变量名字
bookNameField.setAccessible(true);//私有成员变量
bookNameField.set(bookClass, "这是bookName成员变量");//传入类对象和赋值的内容
获取当前Class类型中成员方法并调用
获得所有成员方法
Method[] methods = bookClass.getDeclaredMethods();
for (Method m : methods) {
System.out.println(m);
System.out.println("访问范围:" + (m.getModifiers() == Modifier.PUBLIC ? "公有" : "其它"));
System.out.println("返回类型:" + m.getReturnType());
System.out.println("方法名称:" + m.getName());
}
获得指定成员方法(指定参数类型)并调用
对于private私有成员方法通过
Method 对象 . setAccessible(true)
把权限设置为true使权限检查机制关闭
Method methodDosth2 = bookClass.getDeclaredMethod("dosth2", String.class, double.class);//传入方法名和方法参数.class
methodDosth2.setAccessible(true);
methodDosth2.invoke(bookClass, "平凡的世界", 75.6);//调用方法,传入类类对象和方法参数
注意
getMethod() 经过重载,如果不需要传递参数,例如 getter 方法,那么直接传递方法的 String 字符串即可
Method method = clazz.getMethod("getSid");
如果需要传递参数,比如 setter 方法,那么在后面需要传递参数的类
需要注意的是: 这里没有包装类的自动转换,必须对应,Integer.class 不能写成 int.class,反之亦然
Method method = clazz.getMethod("setSid",int.class);
获取一个类继承的父类和实现的接口(单继承多实现)
Class arrayListClass = ArrayList.class;//获取ArrayList类的Class对象
Class superClass = arrayListClass.getSuperclass();// 获取直接父类,单继承
System.out.println("ArrayList的父类: " + superClass);
Class[] interfaces = arrayListClass.getInterfaces();
System.out.println("ArrayList实现的接口:");//获取接口,多实现
for(Class interfacex : interfaces) {
System.out.println(interfacex);
}
getDeclaredConstructor() 与 getConstructor()
getDeclaredConstructor(Class<?>... parameterTypes)
返回制定参数类型的 所有构造器 ,包括public的和非public的,当然也包括private的
返回结果没有参数类型的过滤
getConstructor(Class<?>... parameterTypes)
只返回制定参数类型访问权限是 public 的构造器。
返回结果同样没有参数类型的过滤
getMethods()/getFileds() 和 getDeclaredMethods()/getDeclaredFields()
获取的方法是无序的
getDeclaredMethods()/getDeclaredFields()
获取本类中 仅自己定义 的所有方法,包括私有的(private、protected、默认以及public)的方法
不包括继承的方法,包括它所实现接口的方法
getMethods()/getFileds()
获取 本类 以及 父类或者父接口 中所有的公共方法(public修饰符修饰的)
getGenericParameterTypes() 和 getParameterTypes()
获取方法形参类型
getGenericParameterTypes:返回Type类型的数组 Type[].
getParameterTypes:返回Class类型的数组: Class<?>[].
关于 Type 接口
Type是一个高级接口
具体的说明来看一段网上的解释:
Type 是 所有类型的高级公共接口,当然也是Class的父类
它们包括原始类型、参数化类型、数组类型、类型变量和基本类型。
先来看一下 Type
的用法:
①
type是一种表示编程语言中的所有类型的类超级接口
:
如:int、Integer、String
这都是表示一编程语言的类型,而其中的 int.class、Integer.class、String.class 它们表示的是类型的实例
②
反射 Class c = Integer.class
,Class相当于表示类型的类
, 而 Integer.class
则是一种名为整形类型的类型实例
③
type 与 class 一样
,不过 type是一种比Class 表示范围还要广的超级接口
,它表示Java语言中类型的所有接口
import java.util.List;
public class SampleClass {
private String sampleField;
private List<Integer> ids;
public String getSampleField() {
return sampleField;
}
public void setSampleField(String sampleField) {
this.sampleField = sampleField;
}
public List<Integer> getIds() {
return ids;
}
public void setIds(List<Integer> ids) {
this.ids = ids;
}
}
public static void main(String[] args) {
Class sampleClassClass = SampleClass.class;
Method[] methods = sampleClassClass.getMethods();
for (Method method : methods) {
System.out.println("------------------" +method.getName());
Type[] genericParameterTypes = method.getGenericParameterTypes();
Class<?>[] parameterTypes = method.getParameterTypes();
for(Class parameterType: parameterTypes){
System.out.println(parameterType + "====" + parameterType.getName());
}
for (int i = 0; i < genericParameterTypes.length; i++) {
System.out.println(genericParameterTypes[i] + "=====" + genericParameterTypes[i].getTypeName());
}
}
String类型,这两个方法返回的结果是一样的
List<Integer> ,getParameterTypes 只返回了类型,泛型没有返回;
而getGenericParameterTypes返回的是完整的泛型。
如果方法参数不是参数化类型(泛型),那么 getParameterTypes 和 getGenericParameterTypes 返回的结果是一样的
如果方法参数是泛型,这时就有区别了,getGenericParameterTypes 会返回 完整的信息 ,而 getParameterTypes 只会返回参数类型,参数化类型无法得到
获取参数化类型
// 将类型向参数化类型转换
ParameterizedType t = (ParameterizedType)genericParameterTypes[0];
// 可以得到参数化类型的参数实例
t.getActualTypeArguments()[0];
其他用法
通过类class文件,拿到该文件里面的资源
TestClass.class
文件(被编译后)所在的classes文件夹
下,有prop.properties
属性文件,如何拿到这个文件的绝对路径?如何把该文件加载到流中
Class clazz = TestClass.class;
ClassLoader classLoader = clazz.getClassLoader();
URL url = classLoader.getResource("prop.properties");
String path = url.getPath();
InputStream ins = new FileInputStream(path);
链式写法为:
String path = TestClass.class.getClassLoader().getResource("prop.properties").getPath();
InputStream ins = new FileInputStream(path);
或者直接将资源加载成流
InputStream ins = TestClass.class.getClassLoader().getSourceAsStream("prop.properties");
用该class文件,创建一个对象
Class xx = TestClass.class;
Object obj = xx.newInstance();//无参构造方法
TestClass tc = (TestClass)obj;//转换为该类的对象
Class xx = TestClass.class;
Constructor cst = xx.getConstructor();//无参构造方法
Object obj = cst.newInstance();//这个也不能有参数
TestClass tc = (TestClass)obj;//转换为该类的对象
上面只能使用无参的构造方法来创建无参默认的对象
,如果想创建带参的对象
呢
Class xx = TestClass.class;
Constructor cst = xx.getConstructor(String.class,int.class);//指定参数类型的带参构造方法
Object obj = cst.newInstance("李四",18);//顺便指定好实参
TestClass tc = (TestClass)obj;//转换为该类的对象
给类设置属性后,再用它执行方法
@Slf4j
public class ExecuteApiUtil {
public static final String jwt = Util.getJwt();
public static final String basePath = "https://xxxx";
public static Object setBasePathForApi(Class<?> api) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
Method setApiClient = api.getMethod("setApiClient", ApiClient.class);
ApiClient apiClient = new ApiClient();
apiClient.setBasePath(basePath);
apiClient.setVerifyingSsl(false);
Object instance = api.newInstance();
setApiClient.invoke(instance, apiClient);
return instance;
}
public static void executeApi(Class<? extends BaseControllerApi> apiClass, String methodName, Object... args) {
try {
Class<?> api = Class.forName(apiClass.getName());
Method[] methods = api.getDeclaredMethods();
Method method = Optional.ofNullable(filterMethod(methods, methodName, args)).orElseThrow(NoSuchMethodException::new);
method.invoke(setBasePathForApi(api), args);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
public static Method filterMethod(Method[] methods, String methodName, Object... args) {
for (Method m : methods) {
String name = m.getName();
if (name.equals(methodName)) {
Parameter[] parameters = m.getParameters();
List<Class<?>> parameterTypes = Lists.newArrayList(m.getParameterTypes());
List<Class<?>> argsList = Arrays.stream(args).collect(Collectors.toList()).stream().map(arg -> arg.getClass()).collect(Collectors.toList());
if (parameters.length == args.length && parameterTypes.equals(argsList)) {
return m;
}
}
}
return null;
}
}
java反射获取方法的参数名
从JDK8开始,反射类添加了Parameter
,通过Parameter类,我们可以从.class编译后的文件中获取方法上参数名。
获取参数名的方法:Parameter.getName()
public static List<String> getParameterNameJava8(Class clazz, String methodName) {
List<String> paramterList = new ArrayList<>();
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
if (methodName.equals(method.getName())) {
//直接通过method就能拿到所有的参数
Parameter[] params = method.getParameters();
for (Parameter parameter : params) {
// 获取当参数的名称
paramterList.add(parameter.getName());
}
}
}
return paramterList;
}
开启javac编译.class文件保留参数名
默认情况下,JDK8的javac编译后的.class文件是不保留方法的参数名
,参数名
是以arg0,arg1,arg3…
表示。
如果要保留参数名,需要给 javac 添加参数 -parameters 开启
在pom.xml
的编译插件中增加参数配置-parameters
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.3version>
<configuration>
<source>1.8source>
<target>1.8target>
<compilerArgs>
<arg>-parametersarg>
compilerArgs>
configuration>
plugin>
Idea配置开启parameters
注意
Object res = method.invoke(instance, args);
Object
对应原方法的返回值
,若原方法无返回值
,此时 method.invoke() 返回null
method.invoke(null,args);
若原方法若为静态方法
,此时形参
Object obj可为null
method.invoke(instance,null);
没参数,可以不传 或者 传 null
若原方法形参列表为空
,则Object[] args为null
Method method = Optional.ofNullable(filterMethod(methods, methodName, args)).orElseThrow(NoSuchMethodException::new);
method.setAccessible(false);
method.invoke(instance, args);
若原方法声明为private
, 则需要在调用此invoke()方法前
,显式调用
方法对象的setAccessible(true)
方法,将可访问private的方法
都为true,内存地址是一样的。在程序的运行中,Person的class文件只被加载了一次。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)