第一句子网 - 唯美句子、句子迷、好句子大全
第一句子网 > 秒懂系列 深入理解Java反射机制

秒懂系列 深入理解Java反射机制

时间:2020-01-17 15:04:32

相关推荐

秒懂系列 深入理解Java反射机制

文章目录

深入理解Java反射机制一、Java反射概述二、Java创建对象思想传递三、类对象3.1 类对象和类的对象3.2 三种获取类对象的方法四、Class对象的常用方法4.1 获取成员变量4.2 获取构造方法4.3 获取方法4.4 获取类名4.5 获取接口4.6 获取包名4.7 获取父类4.8 判断注解五、对象和方法的代工厂5.1 创建对象的代工厂5.2 创建对象并执行方法的代工厂

所以知识体系文章GitHub已收录,欢迎Star!

GitHub地址:/Ziphtracks/JavaLearningmanual

大家记得给我一个Star!笔芯!

搜索关注微信公众号“码出Offer”,Z哥送你学习福利资源!

深入理解Java反射机制

一、Java反射概述

Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。Java反射机制被称为框架设计的灵魂。

二、Java创建对象思想传递

首先在Java反射机制的概述部分,多次强调了任意一个是为什么呢?

这里先想想我们的创建对象过程,创建对象过程需要写一个对象实体类,之后去new这个对象实体类,最后编译、运行文件。而这些过程,我们可以定义为三个重要的阶段。

此三阶段为Source源码阶段 -> Class类加载阶段 -> Runtime运行时阶段

这三个阶段是如何变化的呢?很简单。

首先,我们去写一个Person类,也就是创建一个Person.java文件。其次再去写一个Test测试类,去new一个Person对象。

我们知道,.java文件是需要有一个编译、运行的过程的。所以,我们使用javac命令去对它进行编译操作,这就使得.java文件编译出一个.class字节码文件并存入硬盘中。而这个时期叫做Source源码阶段。

源码阶段过后,我们编译好的.class文件会通过类加载器(ClassLoader)加载到内存中。所以在内存中会描述这个字节码文件为Class类对象。而我们的.class文件中存储的成员变量、构造方法和成员方法等,它们分别有着不同的作用,比如成员变量可以去设置和获取值、构造方法可以去用它创建对象、成员方法可以去运行执行它。而它们会分别装在Filed[]、Constructor[]和Method[]中,那么为什么它们以数组形式存储的呢?那是因为.class文件中写入内存的成员变量、构造方法和成员方法有很多个,所以在内存中的存储方式是数组。

类加载过后,就可以通过类对象的这些行为去创建真正的Person对象了。

这就是一个完整的创建对象的过程。至于上述多此强调任意一个是因为Java反射机制可以在程序的运行过程中(Runtime运行时阶段)操作这些对象,比如:操作FIled、Constructor和Method等对象。此时,反射机制的好处还不只这一个,它还有解耦的好处,可以大大降低程序的紧密程度和耦合性来提高程序的可扩展性。

三、类对象

3.1 类对象和类的对象

根据上述的创建对象思想,我们可以得出反射机制就是程序运行时使用Class类对象封装的方法操作各个对象。所以在这里我们要介绍两个概念类对象类的对象

类对象是类加载的产物,封装了一个类的所有信息(类名、父类、接口、属性、方法、构造方法)类的对象是基于某个类new出来的对象,也称为实例对象

3.2 三种获取类对象的方法

通过类的对象,获取类对象Person person = new Person(); Class clazz = person.getClass();场景:常用于对象的字节码获取方式 通过类名获取类对象Class clazz = 类名.class;场景:常用于参数的传递 以静态方法通过全类名获取类对象Class clazz = Claa.forName("全类名");场景:常用于读取配置文件,将类名定义在配置文件中。读取文件,加载类对象。

以下代码是使用三种方式来获取类对象,然后将三种方式获取的类对象分别比较地址,最后比较完地址后我们发现返回的都是true。这就可以映射出同一个字节码文件(*.class)在一次程序运行过程中,只会类加载一次,不管使用哪一种方式获取的类对象都是同一个。

最后我又将以静态方法通过全类名的方式获取类对象封装成了方法,这样就可以灵活的使用全类名获取类对象了。

package com.mylifes1110.java.bean;class Person {}public class TestReflect {public static void main(String[] args) throws ClassNotFoundException {//通过类的对象获取类对象Person person = new Person();Class<? extends Person> clazz1 = person.getClass();//通过类名获取类对象Class<Person> clazz2 = Person.class;//以静态方法通过全类名获取类对象Class<?> clazz3 = Class.forName("com.mylifes1110.java.bean.Person");System.out.println(clazz1 == clazz2); //trueSystem.out.println(clazz1 == clazz3); //trueSystem.out.println(clazz2 == clazz3); //true}/*** @param className 全类名* @return 以静态方法通过全类名获取类对象*/public static Class getClassObject(String className) {Class clazz = null;try {clazz = Class.forName(className);} catch (ClassNotFoundException e) {e.printStackTrace();}return clazz;}}

四、Class对象的常用方法

4.1 获取成员变量

如下代码我写了详细的使用注释和结果注释,都是通过上述方法实现的。

注意:有一个特殊使用就是访问private修饰的成员变量,需要setAccessible(true);做略成员变量访问修饰符的安全检查处理。如果不做处理的话,就会飘红报错。如下信息:

Exception in thread "main" java.lang.IllegalAccessException: Class com.mylifes1110.java.bean.TestReflectFiled can not access a member of class com.mylifes1110.java.bean.Person with modifiers "private"

该报错信息表示:我们无法访问private修饰的成员变量!

package com.mylifes1110.java.bean;import java.lang.reflect.Field;class Person {private String name;public Integer age;protected Double score;@Overridepublic String toString() {return "Person{" + "name='" + name + '\'' + ", age=" + age + ", score=" + score + '}';}}public class TestReflectFiled {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {Person person = new Person();Class<? extends Person> personClass = person.getClass();//获取 public 修饰的所有成员变量Field[] fields = personClass.getFields();for (Field field : fields) {System.out.println(field);//public java.lang.Integer com.mylifes1110.java.bean.Person.age}//获取 public 修饰的指定名称的成员变量Field age = personClass.getField("age");System.out.println(age); //public java.lang.Integer com.mylifes1110.java.bean.Person.age//获取成员变量age的值Object ageValue = age.get(person);System.out.println(ageValue); //null//设置age的值age.set(person, 18);System.out.println(person); //Person{name='null', age=18, score=null}//获取所有成员变量,不被修饰符限制Field[] declaredFields = personClass.getDeclaredFields();for (Field declaredField : declaredFields) {System.out.println(declaredField);}/*private java.lang.String com.mylifes1110.java.bean.Person.namepublic java.lang.Integer com.mylifes1110.java.bean.Person.ageprotected java.lang.Double com.mylifes1110.java.bean.Person.score*///获取指定名称的成员变量,不被修饰符限制Field name = personClass.getDeclaredField("name");//忽略访问修饰符的安全检查name.setAccessible(true); //暴力反射Object nameValue = name.get(person);System.out.println(nameValue);//null}}

4.2 获取构造方法

如下代码我写了详细的使用注释和结果注释,都是通过上述方法实现的。

注意:因为方法中带Declared字段的方法,就是可以获取private修饰的构造方法,如果通过该构造方法创建实例对象,就需要忽略构造方法访问修饰符的安全检查。此操作是与获取成员变量的解决方式相同,需要setAccessible(true);。所以,在此我就没有演示!

package com.mylifes1110.java.bean;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;class Person {private String name;public Integer age;protected Double score;public Person() {}public Person(String name, Integer age, Double score) {this.name = name;this.age = age;this.score = score;}@Overridepublic String toString() {return "Person{" + "name='" + name + '\'' + ", age=" + age + ", score=" + score + '}';}}public class TestReflectConstructor {public static void main(String[] args) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {Person person = new Person();Class<? extends Person> personClass = person.getClass();//获取 public 修饰的无参构造Constructor<? extends Person> constructor = personClass.getConstructor();System.out.println(constructor); //public com.mylifes1110.java.bean.Person()//通过无参构造创建实例对象Person p1 = constructor.newInstance();System.out.println(p1); //Person{name='null', age=null, score=null}Constructor<? extends Person> constructor2 = personClass.getConstructor(String.class, Integer.class, Double.class);System.out.println(constructor2); //public com.mylifes1110.java.bean.Person(java.lang.String,java.lang.Integer,java.lang.Double)Person p2 = constructor2.newInstance("Ziph", 18, 100.00);System.out.println(p2); //Person{name='Ziph', age=18, score=100.0}//获取 public 修饰的所有构造方法Constructor<?>[] constructors = personClass.getConstructors();for (Constructor<?> constructor1 : constructors) {System.out.println(constructor1);}/*public com.mylifes1110.java.bean.Person()public com.mylifes1110.java.bean.Person(java.lang.String,java.lang.Integer,java.lang.Double)*/}}

4.3 获取方法

如下代码我写了详细的使用注释和结果注释,都是通过上述方法实现的。

注意:因为方法列表中带Declared字段的方法,就是可以获取private修饰的方法,如果通过该方法获取的方法对象并执行,就需要忽略方法访问修饰符的安全检查。此操作是与获取成员变量的解决方式相同,需要setAccessible(true);。所以,在此我也没有演示!

package com.mylifes1110.java.bean;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;class Person {private String name;public Integer age;protected Double score;public void jump() {System.out.println("跳起来。");}public void run(Integer meters) {System.out.println("我今天跑步跑了" + meters + "米。");}@Overridepublic String toString() {return "Person{" + "name='" + name + '\'' + ", age=" + age + ", score=" + score + '}';}}public class TestReflectMethod {public static void main(String[] args) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {Person person = new Person();Class<? extends Person> personClass = person.getClass();//获取 public 修饰的jump无参方法Method jump = personClass.getMethod("jump");//执行获取到的jump无参方法jump.invoke(person);//跳起来。//获取 public 修饰的run有参方法Method run = personClass.getMethod("run", Integer.class);//执行获取到的run有参方法run.invoke(person, 2000); //我今天跑步跑了2000米。//获取 public 修饰的所有方法Method[] methods = personClass.getMethods();for (Method method : methods) {System.out.println(method);//获取方法名System.out.println(method.getName());}/*public void com.mylifes1110.java.bean.Person.run(java.lang.Integer)runpublic java.lang.String com.mylifes1110.java.bean.Person.toString()toStringpublic void com.mylifes1110.java.bean.Person.jump()jumppublic final void java.lang.Object.wait() throws java.lang.InterruptedExceptionwaitpublic final void java.lang.Object.wait(long,int) throws java.lang.InterruptedExceptionwaitpublic final native void java.lang.Object.wait(long) throws java.lang.InterruptedExceptionwaitpublic boolean java.lang.Object.equals(java.lang.Object)equalspublic native int java.lang.Object.hashCode()hashCodepublic final native java.lang.Class java.lang.Object.getClass()getClasspublic final native void java.lang.Object.notify()notifypublic final native void java.lang.Object.notifyAll()notifyAll*/}}

4.4 获取类名

public class TestReflectClassName {public static void main(String[] args) {Person person = new Person();Class<? extends Person> personClass = person.getClass();//获取全类名String className = personClass.getName();System.out.println(className);//com.mylifes1110.java.bean.Person}}

4.5 获取接口

public class TestReflectInterface {public static void main(String[] args) {Person person = new Person();Class<? extends Person> personClass = person.getClass();Class<?>[] interfaces = personClass.getInterfaces();for (Class<?> anInterface : interfaces) {//获取接口System.out.println(anInterface); //interface com.mylifes1110.java.bean.A//判断是否为接口类型System.out.println(anInterface.isInterface()); //true}}}

4.6 获取包名

package com.mylifes1110.java.bean;class Person {}public class TestReflectPackage {public static void main(String[] args) {Person person = new Person();Class<? extends Person> personClass = person.getClass();Package aPackage = personClass.getPackage();System.out.println(aPackage);//package com.mylifes1110.java.bean}}

4.7 获取父类

public class TestReflectSuperClass {public static void main(String[] args) {Person person = new Person();Class<? extends Person> personClass = person.getClass();Class<?> superclass = personClass.getSuperclass();System.out.println(superclass); //class com.mylifes1110.java.bean.Animal}}class Animal {}class Person extends Animal {}

4.8 判断注解

注意:在判断注解是否在某个元素上时,如果时自定义注解,必须将注解的生命周期设置在Runtime运行时期@Retention(RetentionPolicy.RUNTIME),如果不设置该注解无效,判断时则返回false。

public class TestReflectAnnotation {public static void main(String[] args) {Person person = new Person();Class<? extends Person> personClass = person.getClass();//判断abc注解是否在Person类上boolean flag = personClass.isAnnotationPresent(abc.class);System.out.println(flag); //true}}@abcclass Person {}

//自定义注解package com.mylifes1110.java.bean;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;@Retention(RetentionPolicy.RUNTIME)public @interface abc {}

五、对象和方法的代工厂

5.1 创建对象的代工厂

使用Java反射机制实现通过全类名来创建实例对象

/*** 创建对象的工厂模式*/public class TestFactory {public static void main(String[] args) {Object o = createObject("com.mylifes1110.java.bean.A");System.out.println(o.toString());}/*创建对象的工厂*/public static Object createObject(String className) {try {Class c = Class.forName(className);return c.newInstance();} catch (Exception e) {e.printStackTrace();}return null;}}class A {}

5.2 创建对象并执行方法的代工厂

使用Java反射机制来创建对象并封装一个具有普适性的可以获取方法并执行方法的代工厂

public class TestInvokeAnything {public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {Object o = createObject("com.mylifes1110.java.bean.Students");/*创建Students对象并调用exam三参方法*/invokeAnything(o, "exam", new Class[] {int.class, double.class, String.class}, 10, 100, "Ziph");/*创建Students对象并调用study无参方法*/invokeAnything(o, "study", null, null);/*创建Students对象并调用study一参方法*/invokeAnything(o, "study", new Class[] {int.class}, 100);}/*** 利用反射底层技术执行任何方法的通用编程(可以调用每一个方法)* @param obj 对象* @param methodName 方法名称* @param types 方法的形参列表(注意:数组接收)* @param args 需要传入的实参(注意:可变长参数)*/public static void invokeAnything(Object obj, String methodName, Class[] types, Object... args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {/*类对象*/Class c = obj.getClass();/*获取方法的对象Method*/Method method = c.getDeclaredMethod(methodName, types);/*忽略修饰符安全检查*/method.setAccessible(true);/*执行方法*/method.invoke(obj, args);}/*** 创建对象的工厂模式* @param className 需要传入的全类名* @return*/public static Object createObject(String className) {try {Class c = Class.forName(className);return c.newInstance();} catch (Exception e) {e.printStackTrace();}return null;}}class Students {String name;Integer age;String sex;Double score;public Students() {}public Students(String name, Integer age, String sex, Double score) {this.name = name;this.age = age;this.sex = sex;this.score = score;}public void study() {System.out.println("正在学习...");}public int study(int hours) {System.out.println("学习了" + hours + "个小时");return 0;}public void exam(int hours, double score, String name) {System.out.println(name + "做了" + hours + "小时的测验,考了" + score + "分");}public void calc() {System.out.println("Ziph正在计算...");}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。