反射是Java语言中非常重要的一个基础知识点,它的应用往往在于代码的封装上和框架的设计上,对于一般的码农和初级工程师来说,在日常的编码工作中很少直接使用反射,所以不少Java小伙伴对反射是既熟悉又陌生。
泰来网站建设公司成都创新互联公司,泰来网站设计制作,有大型网站制作公司丰富经验。已为泰来1000多家提供企业网站建设服务。企业网站搭建\外贸营销网站建设要多少钱,请找那个售后服务好的泰来做网站的公司定做!
熟悉是都听说过,听说是一个很牛掰的技术,是封装框架,走向架构的必修课,陌生在于日常开发很少直接使用,反正不影响哥们施展CV大法。
如果企业没有编程规范要求和来自领导的review,优化程序是不可能的,重构,向上封装?想都不要想。如果想要让自己的编程功力提高,提升自己的架构思维,那么在日常编码过程中,一定要多去思考,我的代码能否再精简?能否再通用?能否变成一个团队插件?想要实现这个目的,反射就可能派上用场了。
本文会从以下几点为小伙伴解密反射,保障看完神清气爽,直呼哇塞:
Reflection(反射)是Java程序开发语言的重要特性之一,通过反射机制允许在程序运行期间,可以根据任意一个.class【字节码文件】文件获取到这个类的所有信息,包括(成员变量,成员方法,构造器等),并可以通过相应的方式操纵类的字段、方法、构造器
基于反射机制,Java实现在程序运行期间,动态获取信息和动态调用对象的方法,提高了程序的扩展性
使用反射的前提是必须有字节码文件,也就是.class文件,.class文件就是通过.java文件编译而来的。Java作为面向对象的编程语言,提供了为反射准备的字节码文件对象java.lang.Class
反射机制相关的包都在
java.lang.reflect.*;
一些反射中的常用类:
类 |
作用 |
java.lang.Class |
代表整个字节码。代表一个类型,代表整个类 |
java.lang.reflect.Method |
代表字节码中的方法字节码。代表类中的方法 |
java.lang.reflect.Constructor |
代表字节码中的构造方法字节码。代表类中的构造方法 |
java.lang.reflect.Field |
代表字节码中的属性字节码。代表类中的成员变量(静态变量+实例变量) |
还可以通过字节码对象【Class对象】获取注解以下信息,这些信息不在java.lang.reflect包中,所以单独列出来
类 |
作用 |
java.lang.annotation.Annotation |
可以获取类,字段,方法等地方的注解 |
java.lang.Package |
获取所在包 |
java.lang.ClassLoader |
获取类加载器 |
反射是将运行中的class文件读取到并封装进反射类【Class
咱们写的代码是存储在后缀名是 .java的文件里的,但是它会被编译,最终真正去执行的是编译后的 .class文件。Java是面向对象的语言,一切皆对象,所以java认为这些编译后的 class文件也是一种对象,给抽象成了一种类,这个类就是Class,获取反射类有三种方式:
需求:有以下Student类,我们通过三种方式获取该类的Class对象,并根据Class对象的getName方法,输出类的全路径
student类:
package com.stt.reflect;
public class Student {
private Long id;
private String name;
private String sex;
private Integer age;
}
获取反射类:
public class StudentMain {
public static void main(String[] args) {
// 1、通过 getClass 方法获取 Class类,
// 1) 创建Student类对象
Student student = new Student();
// 2) 所有的类都直接或者间接继承Object类,每个对象都有getClass方法
Class studentClazz1 = student.getClass();
System.out.println("studentClazz1===>" + studentClazz1.getName());
// 2、通过.class获取
Class studentClazz2 = Student.class;
System.out.println("studentClazz2===>" + studentClazz2.getName());
// 3、通过Class类的forName方法获取,传入类的全路径
try {
// 此方法抛出 ClassNotFoundException 异常,因为填写的类全路径可能错误,该类可能不存在
Class studentClazz3 = Class.forName("com.stt.reflect.Student");
System.out.println("studentClazz3===>" + studentClazz3.getName());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
输出结果:
方法 |
作用 |
Constructor |
根据参数列表,获取指定数据类型的构造方法,抛出 NoSuchMethodException, SecurityException |
Constructor>[] getConstructors() |
获取所有的构造方法,抛出 SecurityException |
改造Student类,增加四个构造方法,默认有一个无参的构造方法
package com.stt.reflect;
public class Student {
private Long id;
private String name;
private String sex;
private Integer age;
// 无参构造
public Student() {
System.out.println("=======无参构造方法=======");
}
// 单参构造
public Student(String name) {
this.name = name;
System.out.println("=======单参构造方法=======name:" + name);
}
// 两参构造
public Student(Long id, String name) {
this.id = id;
this.name = name;
System.out.println("========两参构造方法=======id:" + id + ", name: " + name);
}
// 三参构造
public Student(Long id, String name, String sex) {
this.id = id;
this.name = name;
this.sex = sex;
System.out.println("========三参构造方法=======id:" + id + ", name: " + name + ", sex: " +sex);
}
// 四参构造
public Student(Long id, String name, String sex, Integer age) {
this.id = id;
this.name = name;
this.sex = sex;
this.age = age;
System.out.println("========三参构造方法=======id:" + id + ", name: " + name + ", sex: " + sex + ", age: " +age);
}
}
package com.stt.reflect;
import java.lang.reflect.Constructor;
public class StudentMain {
public static void main(String[] args) throws Exception {
// 1、获取Class类,这里将异常向上抛出
Class clazz = Class.forName("com.stt.reflect.Student");
// 2、获取无参的构造方法
ConstructornoParamConstructor = clazz.getConstructor();
// 通过Constructor创建对象
Student student1 = noParamConstructor.newInstance();
// 3、获取单灿构造方法,单参是String类型的name,所以参数写为String.class
ConstructoroneStringParamConstructor = clazz.getConstructor(String.class);
Student student2 = oneStringParamConstructor.newInstance("添甄");
// 4、获取两参构造方法,类型分别为Long和String
ConstructortwoStringParamConstructor = clazz.getConstructor(Long.class,String.class);
Student student3 = twoStringParamConstructor.newInstance(1L,"添甄");
}
}
运行结果:
将无参的构造方法设置为私有
private Student() {
System.out.println("=======无参构造方法=======");
}
获取所有构造方法:
package com.stt.reflect;
import java.lang.reflect.Constructor;
public class StudentMain {
public static void main(String[] args) throws Exception {
// 1、获取Class类,这里将异常向上抛出
Class clazz = Class.forName("com.stt.reflect.Student");
// 2、获取所有构造方法
Constructor[] constructors = clazz.getConstructors();
// 3、遍历构造方法
for (Constructorconstructor : constructors) {
System.out.println(constructor);
}
}
}
输出结果:
可以获取所有的公开构造方法,私有构造获取不到
小贴士:通过反射获取私有的构造函数时,用普通的getConstructor()会报错,因为它是私有的,所以提供了专门反射私有构造函数的方法 getDeclaredConstructor(int.class);
如果要调用这个方法还需要设置一下暴力反射才可以,即调用构造器对象的setAccessible(true);方法
获取所有私有方法:
package com.stt.reflect;
import java.lang.reflect.Constructor;
public class StudentMain {
public static void main(String[] args) throws Exception {
// 1、获取Class类,这里将异常向上抛出
Class clazz = Class.forName("com.stt.reflect.Student");
// 2、获取所有构造方法,包括私有
Constructor[] constructors = clazz.getDeclaredConstructors();
// 3、遍历构造方法
for (Constructorconstructor : constructors) {
System.out.println(constructor);
}
}
}
运行结果:
仅仅获取到调用会出现 IllegalAccessException 异常
package com.stt.reflect;
import java.lang.reflect.Constructor;
public class StudentMain {
public static void main(String[] args) throws Exception {
// 1、获取Class类,这里将异常向上抛出
Class clazz = Class.forName("com.stt.reflect.Student");
// 2、获取无参的构造方法,包括私有
Constructorconstructor = clazz.getDeclaredConstructor();
// 3、此时直接调用会报错
Student student = constructor.newInstance();
}
}
报错:
设置暴力破解即可访问:也就是调用一下 setAccessible(true)方法
package com.stt.reflect;
import java.lang.reflect.Constructor;
public class StudentMain {
public static void main(String[] args) throws Exception {
// 1、获取Class类,这里将异常向上抛出
Class clazz = Class.forName("com.stt.reflect.Student");
// 2、获取无参的构造方法,包括私有
Constructorconstructor = clazz.getDeclaredConstructor();
// 3、暴力破解,允许调用私有方法
constructor.setAccessible(true);
// 4、此时直接调用会报错
Student student = constructor.newInstance();
}
}
运行结果:
在Student类中添加以下普通方法
package com.stt.reflect;
public class Student {
// ......
public void speak() {
System.out.println("你好啊!" + name);
}
public Integer getAge() {
System.out.println("getAge===》" + this.age);
return this.age;
}
public void setName(String name) {
this.name = name;
System.out.println("设置名字为:" + name);
}
public static void skill() {
System.out.println("======skill静态方法======");
}
private void method1() {
System.out.println("======私有方法======");
}
private static void method2(String[] args) {
System.out.println("======私有静态方法======" + args);
}
}
获取方法,通过 Method 类型对象封装
package com.stt.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class StudentMain {
public static void main(String[] args) throws Exception {
// 1、获取Class类,这里将异常向上抛出
Class clazz = Class.forName("com.stt.reflect.Student");
// 2、获取所有非私有方法,包括父类中方法
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method);
}
}
}
如下图:获取到了所有的非私有方法,包括父类中的方法
通过调用getDeclaredMethods方法获取所有方法,
package com.stt.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class StudentMain {
public static void main(String[] args) throws Exception {
// 1、获取Class类,这里将异常向上抛出
Class clazz = Class.forName("com.stt.reflect.Student");
// 2、获取所有非私有方法,包括父类中方法
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method);
}
}
}
如下,获取了包括私有方法,但是不包括父类中的方法
我们可以通过 getMethod(String name, Class>... parameterTypes) 方法,根据方法名和参数获取方法,在通过Method对象的 invoke(Object obj, Object... args)方法调用方法,如果是私有方法,你懂的,需要暴力破解一下再调用,和构造器一样
package com.stt.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class StudentMain {
public static void main(String[] args) throws Exception {
// 1、获取Class类,这里将异常向上抛出
Class clazz = Class.forName("com.stt.reflect.Student");
// 2、获取无参的 speak 方法
Method speakMethod = clazz.getMethod("speak");
// 3、创建Student对象
Constructorconstructor = clazz.getConstructor(Long.class,String.class,String.class,Integer.class);
Student student = constructor.newInstance(1L,"添甄","男",26);
// 传入 student,意为要调用student对象的speak
Object returnResult = speakMethod.invoke(student);
System.out.println("方法返回值==》" + returnResult);
// 4、调用getAge方法,没有参数有返回值
Method ageMethod = clazz.getMethod("getAge");
Integer returnAge = (Integer)ageMethod.invoke(student);
System.out.println(returnAge);
// 5、调用有参数的方法,getMethod参数2写为参数的类型
Method setNameMethod = clazz.getMethod("setName",String.class);
setNameMethod.invoke(student, "王小波");
}
}
方式和调用私有构造器一致,首先通过 getDeclaredMethod 获取私有方法,其次调用 setAccessible(true) 设置可访问为true
package com.stt.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class StudentMain {
public static void main(String[] args) throws Exception {
// 1、获取Class类,这里将异常向上抛出
Class clazz = Class.forName("com.stt.reflect.Student");
// 2、创建Student对象
Constructorconstructor = clazz.getConstructor(Long.class,String.class,String.class,Integer.class);
Student student = constructor.newInstance(1L,"添甄","男",26);
// 3、通过 getDeclaredMethod 获取私有方法 private void method1()
Method method1 = clazz.getDeclaredMethod("method1");
// 设置为可访问 true
method1.setAccessible(true);
// 调用方法
Object invoke = method1.invoke(student);
}
}
参数维数组有点特殊,这是因为 jdk1.4和jdk1.5处理invoke方法有区别
1.5:public Object invoke(Object obj,Object…args):1.5参数为Object类型的可变参数
1.4:public Object invoke(Object obj,Object[] args):1.4参数为一个Object类型数组
在反射方法时,如果方法的参数是一个数组,考虑到向下兼容问题,会按照JDK1.4的语法来对待(JVM会把传递的数组参数拆开,拆开就会报参数的个数不匹配的错误) 解决办法:防止JVM拆开你的数组 方式一:把数组看做是一个Object对象 方式二:重新构建一个Object数组,那个参数数组作为唯一的元素存在。
package com.stt.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class StudentMain {
public static void main(String[] args) throws Exception {
// 1、获取Class类,这里将异常向上抛出
Class clazz = Class.forName("com.stt.reflect.Student");
// 2、创建Student对象
Constructorconstructor = clazz.getConstructor(Long.class,String.class,String.class,Integer.class);
Student student = constructor.newInstance(1L,"添甄","男",26);
// 3、通过 getDeclaredMethod 获取私有方法 private void method1()
Method method1 = clazz.getDeclaredMethod("method2",String[].class);
// 设置为可访问 true
method1.setAccessible(true);
// 调用方式1:将数组强转为Object对象
method1.invoke(student,(Object)new String[]{"a","b"});
// 调用方式2:将数组存储进Object数组中
method1.invoke(student,new Object[]{new String[]{"a","b"}});
}
}
错误调用:如果直接传进数组,则会出现IllegalArgumentException【非法参数异常】报错
Method method1 = clazz.getDeclaredMethod("method2",String[].class);
// 设置为可访问 true
method1.setAccessible(true);
// 错误调用:直接传递数组
method1.invoke(student,new String[]{"a","b"});
设置name属性为public
private Long id;
public String name;
private String sex;
private Integer age;
相关方法:
方法 |
作用 |
Field getField(String name) |
获取指定非私有属性 |
Field[] getFields() |
获取所有非私有属性 |
Field getDeclaredField(String name) |
获取指定私有属性 |
Field[] getDeclaredFields() |
获取所有私有属性 |
获取属性并通过get方法获取属性值
package com.stt.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class StudentMain {
public static void main(String[] args) throws Exception {
// 1、获取Class类,这里将异常向上抛出
Class clazz = Class.forName("com.stt.reflect.Student");
// 2、创建Student对象
Constructorconstructor = clazz.getConstructor(Long.class,String.class,String.class,Integer.class);
Student student = constructor.newInstance(1L,"添甄","男",26);
// 3、获取name属性
Field nameField = clazz.getField("name");
// 4、获取所有费私有属性
Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.println("field name====》" + field.getName());
System.out.println("字段值==》" + field.get(student));
}
// 5、获取指定私有属性
Field sexField = clazz.getDeclaredField("sex");
sexField.setAccessible(true);
System.out.println(sexField.get(student));
}
}
运行结果:
使用Servlet编写web应用时,往往都有一个web.xml文件,
my3
com.servlet.MyServlet3
my3
本文标题:原来高手是这么回答和使用反射的,又觉得涨了不少知识
网页链接:http://www.csdahua.cn/qtweb/news41/214691.html网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网