System.out.println(“类的名称:” + mClass.getName());
//2.1 获取所有 public 访问权限的方法
//包括自己声明和从父类继承的
Method[] mMethods = mClass.getMethods();
//2.2 获取所有本类的的方法(不问访问权限)
//Method[] mMethods = mClass.getDeclaredMethods();
//3.遍历所有方法
for (Method method :
mMethods) {
//获取并输出方法的访问权限(Modifiers:修饰符)
int modifiers = method.getModifiers();
System.out.print(Modifier.toString(modifiers) + " ");
//获取并输出方法的返回值类型
Class returnType = method.getReturnType();
System.out.print(returnType.getName() + " "
- method.getName() + "( ");
//获取并输出方法的所有参数
Parameter[] parameters = method.getParameters();
for (Parameter parameter:
parameters) {
System.out.print(parameter.getType().getName()
- " " + parameter.getName() + “,”);
}
//获取并输出方法抛出的异常
Class[] exceptionTypes = method.getExceptionTypes();
if (exceptionTypes.length == 0){
System.out.println(" )");
}
else {
for (Class c : exceptionTypes) {
System.out.println(" ) throws "
- c.getName());
}
}
}
}
同获取变量信息一样,需要注意注释中 2.1 与 2.2 的区别,下面看一下打印输出:
- 调用
getMethods()
方法
获取 SonClass
类所有 public
访问权限的方法,包括从父类继承的。打印信息中,printSonMsg()
方法来自 SonClass
类, printFatherMsg()
来自 FatherClass
类,其余方法来自 Object 类。
类的名称:obj.SonClass
public void printSonMsg( )
public void printFatherMsg( )
public final void wait( ) throws java.lang.InterruptedException
public final void wait( long arg0,int arg1, ) throws java.lang.InterruptedException
public final native void wait( long arg0, ) throws java.lang.InterruptedException
public boolean equals( java.lang.Object arg0, )
public java.lang.String toString( )
public native int hashCode( )
public final native java.lang.Class getClass( )
public final native void notify( )
public final native void notifyAll( )
- 调用
getDeclaredMethods()
方法
打印信息中,输出的都是 SonClass
类的方法,不问访问权限。
类的名称:obj.SonClass
private int getSonAge( )
private void setSonAge( int arg0, )
public void printSonMsg( )
private void setSonName( java.lang.String arg0, )
private java.lang.String getSonName( )
三、访问或操作类的私有变量和方法
================
在上面,我们成功获取了类的变量和方法信息,验证了在运行时 动态的获取信息 的观点。那么,仅仅是获取信息吗?我们接着往后看。
都知道,对象是无法访问或操作类的私有变量和方法的,但是,通过反射,我们就可以做到。没错,反射可以做到!下面,让我们一起探讨如何利用反射访问 类对象的私有方法 以及修改 私有变量或常量。
老规矩,先上测试类。
注:
-
请注意看测试类中变量和方法的修饰符(访问权限);
-
测试类仅供测试,不提倡实际开发时这么写 : )
TestClass.java
3.1 访问私有方法
以访问 TestClass
类中的私有方法 privateMethod(...)
为例,方法加参数是为了考虑最全的情况,很贴心有木有?先贴代码,看注释,最后我会重点解释部分代码。
/**
-
访问对象的私有方法
-
为简洁代码,在方法上抛出总的异常,实际开发别这样
*/
private static void getPrivateMethod() throws Exception{
//1. 获取 Class 类实例
TestClass testClass = new TestClass();
Class mClass = testClass.getClass();
//2. 获取私有方法
//第一个参数为要获取的私有方法的名称
//第二个为要获取方法的参数的类型,参数为 Class…,没有参数就是null
//方法参数也可这么写 :new Class[]{String.class , int.class}
Method privateMethod =
mClass.getDeclaredMethod(“privateMethod”, String.class, int.class);
//3. 开始操作方法
if (privateMethod != null) {
//获取私有方法的访问权
//只是获取访问权,并不是修改实际权限
privateMethod.setAccessible(true);
//使用 invoke 反射调用私有方法
//privateMethod 是获取到的私有方法
//testClass 要操作的对象
//后面两个参数传实参
privateMethod.invoke(testClass, "Java Reflect ", 666);
}
}
需要注意的是,第3步中的 setAccessible(true)
方法,是获取私有方法的访问权限,如果不加会报异常 IllegalAccessException,因为当前方法访问权限是“private”的,如下:
java.lang.IllegalAccessException: Class MainClass can not access a member of class obj.TestClass with modifiers “private”
正常运行后,打印如下,调用私有方法成功:
Java Reflect 666
3.2 修改私有变量
以修改 TestClass
类中的私有变量 MSG
为例,其初始值为 “Original” ,我们要修改为 “Modified”。老规矩,先上代码看注释。
/**
-
修改对象私有变量的值
-
为简洁代码,在方法上抛出总的异常
*/
private static void modifyPrivateFiled() throws Exception {
//1. 获取 Class 类实例
TestClass testClass = new TestClass();
Class mClass = testClass.getClass();
//2. 获取私有变量
Field privateField = mClass.getDeclaredField(“MSG”);
//3. 操作私有变量
if (privateField != null) {
//获取私有变量的访问权
privateField.setAccessible(true);
//修改私有变量,并输出以测试
System.out.println("Before Modify:MSG = " + testClass.getMsg());
//调用 set(object , value) 修改变量的值
//privateField 是获取到的私有变量
//testClass 要操作的对象
//“Modified” 为要修改成的值
privateField.set(testClass, “Modified”);
System.out.println("After Modify:MSG = " + testClass.getMsg());
}
}
此处代码和访问私有方法的逻辑差不多,就不再赘述,从输出信息看出 修改私有变量 成功:
Before Modify:MSG = Original
After Modify:MSG = Modified
3.3 修改私有常量
在 3.2 中,我们介绍了如何修改私有 变量,现在来说说如何修改私有 常量,
01. 真的能修改吗?
常量是指使用 final
修饰符修饰的成员属性,与变量的区别就在于有无 final
关键字修饰。在说之前,先补充一个知识点。
Java 虚拟机(JVM)在编译 .java
文件得到 .class
文件时,会优化我们的代码以提升效率。其中一个优化就是:JVM 在编译阶段会把引用常量的代码替换成具体的常量值,如下所示(部分代码)。
编译前的 .java
文件:
//注意是 String 类型的值
private final String FINAL_VALUE = “hello”;
if(FINAL_VALUE.equals(“world”)){
//do something
}
编译后得到的 .class
文件(当然,编译后是没有注释的):
private final String FINAL_VALUE = “hello”;
//替换为"hello"
if(“hello”.equals(“world”)){
//do something
}
但是,并不是所有常量都会优化。经测试对于 int
、long
、boolean
以及 String
这些基本类型 JVM 会优化,而对于 Integer
、Long
、Boolean
这种包装类型,或者其他诸如 Date
、Object
类型则不会被优化。
总结来说:对于基本类型的静态常量,JVM 在编译阶段会把引用此常量的代码替换成具体的常量值。
这么说来,在实际开发中,如果我们想修改某个类的常量值,恰好那个常量是基本类型的,岂不是无能为力了?反正我个人认为除非修改源码,否则真没办法!
这里所谓的无能为力是指:我们在程序运行时刻依然可以使用反射修改常量的值(后面会代码验证),但是 JVM 在编译阶段得到的 .class 文件已经将常量优化为具体的值,在运行阶段就直接使用具体的值了,所以即使修改了常量的值也已经毫无意义了。
下面我们验证这一点,在测试类 TestClass
类中添加如下代码:
接下来,是修改常量的值,先上代码,请仔细看注释:
/**
-
修改对象私有常量的值
-
为简洁代码,在方法上抛出总的异常,实际开发别这样
*/
private static void modifyFinalFiled() throws Exception {
//1. 获取 Class 类实例
TestClass testClass = new TestClass();
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
最后
金三银四到了,送上一个小福利!
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!
正体系化!**
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
最后
金三银四到了,送上一个小福利!
[外链图片转存中…(img-DiIbxkvk-1711985355565)]
[外链图片转存中…(img-SNXV2V6l-1711985355565)]
[外链图片转存中…(img-zKiAdW7V-1711985355566)]
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!