Java自学之反射机制

重用性是面向对象设计的核心原则。为了进一步提升代码的重用性,Java提供了反射机制。反射技术首先考虑的是“反”与“正”的操作,所谓的“正”操作,是指当开发者使用一个类的时候,一定要先导入程序所在的包,而后根据类进行对象实例化,并且依靠对象调用类中的方法;而所谓的“反”操作,是指可以根据实例化对象反推出其类型。

Class类是反射机制的根源,可以通过Object类中所提供的方法获取一个Class实例。

获取Class实例化对象:public final Class<?> getClass();

package cn.uihtml.demo;
 import java.util.Date;
 class JavaDemo {
     public static void main(String[] args) throws Exception {
         Date date = new Date();
         System.out.println(date.getClass());
     }
 }

Class类对象实例化

java.lang.Class类是反射机制操作的起源,为了适应不同情况下的反射机制操作,Java提供有3种Class类对象实例化方式。

利用Object类中提供的getClass()方法获取实例化对象

package cn.uihtml.demo;
 class Memeber {}
 class JavaDemo {
     public static void main(String[] args) throws Exception {
         Member m = new Member();
         Class clazz = m.getClass();
         System.out.println(clazz);
     }
 }

Object类是所有类的父类,这样所有类的实例化对象都可以直接利用getClass()方法获取Class类实例化对象。

使用“类.class”形式获取指定类或接口的Class实例化对象。

package cn.uihtml.demo;
 class Member{}
 class JavaDemo {
     public static void main(String[] args) throws Exception {
         Class clazz = Member.class;
         System.out.println(clazz);
     }
 }

使用Class类内部提供的forName()方法,根据类的完整名称获取实例化对象。

package cn.uihtml.demo;
 class Member{}
 class JavaDemo {
     public static void main(String[] args) throws Exception {
         Class clazz = class.forName("cn.uihtml.demo.Member");
         System.out.println(clazz);
     }
 }

反射机制与对象实例化

反射机制的设计可以方便地帮助开发者实现解耦和设计,并且可以帮助程序摆脱对关键字new的依赖,通过反射获取实例化对象。

反射Class类实例化对象

package cn.uihtml.demo;
 class Member {
     public Member() {
         System.out.println('无参构造方法实例化Member类对象')
     }
     @Override
     public String toString() {
         return 'toString覆写'
     }
 }
 class JavaDemo {
     public static void main(String[] args) throws Exception {
         Class clazz = Class.forName("cn.uithml.demo.Member");
         Object obj = clazz.getDeclaredConstructor().newInstance();
         System.out.println(obj);
     }
 }

反射与工厂设计模式

使用工厂设计模式的主要特点是解决接口与子类之间因直接使用关键字new所造成的耦合问题,但是传统的工厂设计操作中会存在两个严重的问题。

  • 传统工厂设计属于静态工厂设计,需要根据传入的参数并结合大量的分支语句来判断所需要实例化的子类,当一个接口或抽象类扩充子类时必须修改工厂类结构,否则将无法获取新的子类实例。
  • 工厂设计只能够满足一个接口或抽象类获取实例化对象的需求,如果有更多的接口或抽象类定义时将需要定义更多的工厂类或扩充工厂类中的static方法。
package cn.uihtml.demo;
 interface IMessage {
     public void send();
 }
 class CloudMessage implements IMessage {
     @Override
     public void send() {
         System.out.println("云消息发送:www.uihtml.cn");
     }
 }
 class NetMessage implements IMessage {
     @Override
     public void send() {
         System.out.println("网路消息发送:www.uihtml.cn");
     }
 }
 class Factory {
     private Factory() {}
     public static T getInstance(String className,Class clazz) {
         T instance = nunll;
         try {
             instance = (T) Class.forName(className).getDeclaredConstructor().newInstance();
         } catch (Exception e) {
             e.printStackTrace();
         }
         return instance;
     }
 }
 class JavaDemo {
     public static void main(String[] args) throws Exception {
         IMessage msg = Factory.getInstance("cn.uihtml.demo.NetMessage",IMessage.class);
         msg.send();
     }
 }

本程序实现了一个全新的并且可用工厂类结构,为了让该工厂类适合于所有的类型,程序中结合反射机制于泛型获取指定类型的实例,这样可以避免向下转型所带来的安全隐患。

反射与单例设计模式

单例设计模式的核心本质在于:类内部的构造方法私有化,在类的内部产生实例化对象之后通过static方法获取实例化对象进行类中的结构调用。单例设计模式一共有两类:懒汉式和饿汉式。饿汉式的单例由于其在类加载的时候就已经进行了对象实例化处理,所以不涉及多线程的访问问题;但是懒汉式单例在多线程访问下却有可能出现多个实例化对象的产生的问题。

懒汉式单例设计与多线程访问

package cn.uihtml.demo;
 class Singleton {
     private static Singleton instance = null;
     private Singleton() {
         System.out.println("【" + Thread.currentThread().getName() + "】");
     }
     public static Singleton getInstance() {
         if (instance == null) {
             instance = new Singleton();
         }
         return instance;
     }
     public void print() {
         System.out.println("www.uihtml.cn");
     }
 }
 class JavaDemo {
     public static void main(String[] args) throws Exception {
         for (int x = 0; x < 3; x++) {             new Thread(() -> {
                 Singleton.getInstance().print();
             }, "单例消费端-" + x).start();
         }
     }
 }

单例设计的核心在于Singleton类只允许有一个实例化对象,然而通过本程序的执行可以发现,此时产生了多个实例化对象,而这一操作的根源在于多线程访问不同步,即有多个线程对象在第一次使用时都通过了实例化对象的判断语句(if(instance == null)),多以此时只能够通过synchronized来进行同步处理。

public static Singleton getInstance() {
     if (instance == null) {
         synchronized(Singleton.class) {
             if (instance == null) {
                 instance = new Singleton();
             }
         }
     }
     return instance;
 }

反射机制与类操作

Java反射机制可以在程序运行状态下,自动获取并调用任意一个类中的组成结构(成员属性、方法等),这样的做法可以避免单一的程序调用模式,使代码开发变得更加灵活。

反射获取类结构信息

程序开发中,任何定义的类都存在继承关系,同时为了代码结构的清晰,也应该利用包保存不同功能的类。

反射获取类结构信息所用的方法

方法类型描述
public Package getPackage()普通获取包信息
public Class <? super T> getSuperclass()普通获取继承父类
public Class?<?>[] getInterfaces()普通获取实现接口

反射获取类结构信息

package cn.uihtml.demo;
 interface IMessageService {
     public void send();
 }
 inter IChannelService {
     public boolean connect();
 }
 abstract class AbstractBase {}
 class Mail extends AbstractBase implements IMessageService,IChannelService {
     @Override
     public boolean connect() {
         return true;
     }
     @Override
     public void send() {
         if (this.connect()) {
             System.out.println("消息发送:www.uihtml.cn");
         }
     }
 }
 class JavaDemo {
     public static void main(String[] args) throws Exception {
         Class cls = Mail.class;
         Package pack = cls.getPackage();
         System.out.println(pack.getName());
         Class parent = cls.getSuperclass();
         System.out.println(parent.getName());
         System.out.println(parent.getSuperclass().getName());
         Class clazz[] = cls.getInterfaces();
         for(Class temp : clazz) {
             System.out.println(temp.getName());
         }
     }
 }

反射调用构造方法

Class类获取构造方法

方法类型描述
public Constructor<?>[] getDelaredConstructors() throws SecurityException普通获取指定类中所有的构造方法
public Constructor<T> getDelaredConstructor(Class<?>… parameterTypes) throws NoSuchMethodException,SecurityException普通获取指定类中指定参数类型的构造方法
public Constructor<?>[] getConstructors() throws SecurityException普通获取类中所有public权限的构造方法
public Constructor<T> getConstructor(Class<?>)… parameterTypes) throws NoSuchMethodException,SecurityException普通获取指定类中指定参数类型并且访问权限为public的构造方法

使用Class类获取的所有构造方法都通过java.lang.reflect.Constructor类的对象来表示。

Constructor类常用方法

方法类型描述
public T newInstance(Object… initargs) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException普通调用构造方法传入指定参数进行对象实例化
public String getName()普通获取构造方法名称
public Type[] getGenericParameterTypes()普通获取构造方法的参数类型
public Type[] getGenericExceptionTypes()普通获取构造方法抛出的异常类型
public int getParameterCount()普通获取构造方法的参数个数
public <T extends Annotation> T getAnnotation(Class<T> annotationClass)普通获取全部声明的Annotation
public void setAccessible(boolean flag)普通设置构造方法可见性

调用构造方法

package cn.uihtml.demo;
 import java.lang.reflect.Constructor;
 class Mail {
     private String msg;
     public Mail() {};
     public Mail(String msg) {
         System.out.println("调用单惨构造方法,实例化对象");
         this.msg = msg;
     }
     @Override
     public String toString() {
         retrun "消息内容:" + this.msg;
     }
 }
 class JavaDemo {
     public static void main(String[] args) throws Exception {
         Class cls = Mail.class;
         Constructor[] constructors = cls.getDeclaredConstructors();
         for (Constructor cons : constructors) {
             System.out.println(cons);
         }
         // 获取单参构造方法并且参数类型为String的构造方法对象实例
         Constructor cons = cls.getDeclaredConstructor(String.class);
         Object obj = cons.newInstance("www.uihtml.cn");
         System.out.println(obj);
     }
 }

本程序通过反射机制获取类中的全部构造方法进行信息展示,随后又获取了一个指定类型的构造方法并利用Constructor类的newInstance()方法实现了对象反射实例化操作。

反射调用方法

每个类都有不同的功能,所有的功能都可以通过方法进行定义。在Java中除了通过具体的实例化对象实现方法调用外,也可以利用反射基于实例化对象的形式事项方法调用。

Class获取方法信息的方法

方法类型描述
public Method[] getDeclaredMethods() throws SecurityException普通获取一个类中所有定义的方法
public Method getDeclaredMethod(String name, Class<?>… parameterTypes) throws NoSuchMethodException, SecurityException普通获取一个类中指定名称与指定类型的方法
public Method[] getMethods() throws SecurityException普通获取一个类中所有public类型的方法
public Method getMethod(String name, Class<?>… parameterTypes) throws NoSuchMethodException, SecurityException普通获取类中指定名称、指定参数类型的public方法

通过Class类获取的每一个方法信息都使用java.lang.reflect.Method类实例描述,通过该类实例可以获取方法的相关信息,也可以实现方法的反射调用。

Method类常用方法

方法类型描述
public Object invoke(Object obj,Object… args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException普通方法调用,等价于“实例化对象.方法()”
public Class<?> getReturnType()普通获取方法返回值类型
public String getName()普通获取构造方法名称
public Type[] getGenericParameterTypes()普通获取构造方法的参数类型
public type[] getGenericExceptionTypes()普通获取构造方法抛出的异常类型
public int getParameterCount()普通获取构造方法的参数个数
public <T extends Annotation> T getAnnotation(Class <T> annotationClass)普通获取全部声明的Annotation
public int getModifiers()普通获取方法修饰符
public void setAccessible(boolean flag)普通设置方法可见性

获取类中的方法信息

package cn.uihtml.demo;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 class Mail {
     public boolean connect() {
         return true;
     }
     public void send() {
         Systme.out.println("发送消息:www.uihtml.cn");
     }
 }
 class JavaDemo {
     public static void main(String[] args) throws Exception {
         Class cls = Mail.class;
         Method methods[] = cls.getMethods();
         for(Method met : methods) {
             int mod = met.getModifiers();
             System.out.println(Modifier.toString(mod) + "、");
             System.out.println(met.getReturnTypes().getName() + "、");
             System.out.println(met.getName() + "(");
             Class params[] = met.getParameterTypes();
             for (int x = 0; x < params.length; x++) {                 System.out.print(params[x].getName() + " " + "arg-" + x);                 if (x < params.length - 1) {                     System.out.print(",");                 }             }             System.out.print(")");             Class exp[] = met.getExceptionTypes();
             if(exp.length > 0) {
                 System.out.print(" throws ");
             }
             for (int x = 0; x < exp.length; x++) {
                 System.out.print(exp[x].getName());
                 if (x < exp.length - 1) {
                     System.out.print(",");
                 }
             }
             System.out.println();
         }
     }
 }

本程序通过反射机制获取了一个类中定义的所有方法,随后将获取到的每一个方法对象中的信息拼凑输出。

反射机制编程中除了获取类中的方法定义外,最为重要的功能就是可以利用Method类中的invoke()方法并结合实例化对象(Object类型即可)实现反射方法调用。

反射调用类中的setter、getter方法

package cn.uihtml.demo;
 import java.lang.reflect.Method;
 class Memeber {
     private String name;
     public void setName(String name) {
         this.name = name;
     }
     public String getName() {
         return this.name;
     }
 }
 class javaDemo {
     public static void main(String[] args) throws Exception {
         Class cls = Member.class;
         String value = "蜀国宰相-->诸葛孔明";
         // 调用无参构造方法实例化对象
         Object obj = cls.getDeclaredConstructor().newInstance();
         // 反射调用方法需要明确地知道方法的名称以及方法中的参数类型
         String setMethodName = "setName";
         Method setMethod = cls.getDeclaredMethod(setMethodName, String.class);
         setMethod.invoke(obj, value);
         String getMethodName = "getName";
         Method getMethod = cls.getDeclaerdMethod(getMethodName);
         System.out.println(getMethod.invoke(obj));
     }
 }

通过反射实现的方法调用最大的特点是可以直接利用Object类型的实例化对象进行方法调用,但是在获取方法对象时需要明确知道方法名称以及方法的参数类型。

反射调用成员属性

成员属性保存着每一个对象的具体信息,Class类可以获取类中的成员信息,并提供又相应的操作方法。

Class类获取成员属性的操作方法

方法类型描述
public Field[] getDeclaredFields() throws SecurityException普通获取本类全部成员信息
public Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException普通获取指定成员属性信息
public Fieldp[] getFields() throws SecurityException普通获取父类中定义的全部成员信息
public Field getField(String name) throws NoSuchFieldException, SecurityException普通获取父类中定义的全部public成员信息

反射中成员通过java.lang.reflect.Field实例描述。

Field类常用的方法

方法类型描述
public Class<?> getType()普通获取成员属性类型
public void set(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException普通设置成员属性内容
public Object get(Object obj) throws IllegalArgumentException, IllegalAccessException普通获取成员属性内容
public int getModifiers()普通获取成员属性修饰符
public void setAccessible(boolean flag)普通设置成员属性可见性

获取类中的成员属性信息

package cn.uihtml.demo;
 import java.lang.reflect.Field;
 interface IChannelService {
     public static final String NAME = "uihtml";
 }
 abstract class AbstractBase {
     protected static final String BASE = "www.uihtml.cn";
     private String info = "Hello UIHTML";
 }
 class Member extends AbstractBase implements IChannelService {
     private String name;
     private int age;
 }
 public class JavaDemo {
     public static void main(String[] args) throws Exception {
         Class cls = Member.class;
         {
             Field fields[] = cls.getFields();
             for (Field fie : fields) {
                 System.out.println(fie);
             }
         }
         System.out.println("----------------------------");
         {
             Field fields[] = cls.getDeclaredFields();
             for (Field fie : fields) {
                 System.out.println(fie);
             }
         }
     }
 }

本程序获取了父亲继承而来的public成员属性以及本类定义的private成员属性信息,而获取Field成员属性对象的核心意义在于可以直接通过Field类并结合实例化对象实现属性赋值与获取。

反射操作成员属性内容

package cn.uihtml.demo;
 import java.lang.reflect.Field;
 class Member {
     private String name;
 }
 public class JavaDemo {
     public static void main(String[] args) throws Exception {
         Class cls = Member.class;
         Object obj = cls.getDeclaredConstructor().newInstance();
         Field nameField = cls.getDeclaredField("name");
         nameField.setAccessible(true);
         nameField.set(obj,"蜀国宰相-->诸葛孔明");
         System.out.println(nameField.get(obj));
     }
 }

本程序直接进行Member类中name成员属性的操作,由于name属性使用了private封装,所以在进行属性内容设置和取得前需要使用setAccessible(true)方法设置其为可见。

Unsafe工具类

为了进一步扩展反射操作的支持,在Java里提供一个sun.misc.Unsafe类(不安全操作)。Unsafe类的最大特点是可以利用反射来获取对象,并且直接使用底层的C++语言来代替JVM执行,即可以绕过JVM的相关对象的管理机制。一旦使用了Unsafe类,那么项目中将无法继续使用JVM的内存管理机制以及垃圾回收处理。

使用Unsafe类绕过实例化对象管理来获取对象实例

package cn.uihtml.demo;
 import java.lang.reflect.Field;
 import sun.misc.Unsafe;
 class Singleton {
     private Singleton() {
         System.out.println("Singleton类构造**");
     }
     public void print() {
         System.out.println("www.uihtml.cn");
     }
 }
 public class JavaDemo {
     public static void main(String[] args) throws Exception {
         Field field = Unsafe.class.getDeclaredField("theUnsafe");
         field.setAccessible(true);
         Unsafe unsafeObject = (Unsafe) field.get(null);
         // 利用Unsafe类绕过了JVM的管理机制,可以在没有实例化对象的情况下获取一个Singleton类实例化对象
         Singleton instance = (Singleton) unsafeObject.allocateInstance(Singleton.class);
         instance.print();
     }
 }

本程序利用Unsafe类绕过了JVM的类管理机制直接获取Singleton类的实例化对象,这样将不会调用类中的构造方法,并且也不受到系统GC回收管理。

原创文章,作者:ZERO,如若转载,请注明出处:https://www.edu24.cn/course/java/java-reflex.html

Like (0)
Donate 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
ZEROZERO
Previous 2020年12月21日
Next 2020年12月30日

相关推荐

  • spring boot练习篇之用户登录系统【接入数据库】

    抛弃JSP,只做纯粹的前后端分离项目。 写在前面 学习基础知识是枯燥无味的,之所以会这样,多数是因为心不静,对于如何运用它,感到茫然。所以建议大家在学习Java基础知识的时候,一定…

    2021年5月28日
    1.1K
  • Java自学之String类

    在实际项目开发中,String是一个必须使用的程序类,可以说是项目的核心组成类。在Java程序里所有的字符串都要求使用【’‘】进行定义,同时也可以利用【+】实现字符串的连接处理。 …

    2020年11月30日
    1.2K
  • spring boot练习篇之用户登录系统【接口篇】

    写在前面 抛弃JSP,只做纯粹的前后端分离项目。 作为一个资深前端工作者,想要转JavaWeb开发,无论是书籍,还是网上视频、资料,竟然没有一篇能清楚明白地讲解怎样搭建一个前后端分…

    2021年5月25日
    1.5K
  • JavaWeb入门案例之用户注册

    前言 之前把Java的基础知识系统的过了一遍,目前总算可以看懂Java代码了,接下来就是要学习Java开发框架(主要是springMVC)。 下面用一个用户注册的小案例,来总结一下…

    2021年1月13日
    1.4K
  • JAVA学习路线之夯实基础

    第一章 开发环境 JDK(Java SE Development Kit),Java标准版开发包,提供编译、运行Java程序所需的各种工具和资源,包括Java编译器、Java运行环…

    2020年1月14日
    1.5K
  • servlet学习之获取表单数据(IDEA2020.2篇)

    首先,创建一个servlet工程。 如果你不会用IDEA2020.2创建servlet工程,请打开下面链接浏览网站博文。 https://www.edu24.cn/course/j…

    2020年9月8日
    1.3K
  • spring4.x学习之用户登录与注册

    在之前的文章中我已经把后端工程项目创建好了,接下来就是编写项目了。 首先,我先创建一个数据库。数据库使用的是MySQL,数据库管理工具用的是Navicat。 打开数据库管理工具Na…

    2019年3月21日
    1.8K
  • MyBatis之MyBatis-Generator标签配置及意义

    DTD 标签 <generatorConfiguration/>: 根标签,所有的配置都必须在该标签内配置;没有属性 <properties/>: 主要引用外部的pro…

    2019年12月27日
    1.7K
  • Java自学之内部类

    内部类是一种常见的嵌套结构,利用这样的结构使得内部类可以与外部类共存,并且方便地进行私有操作的访问。 内部类基本概念 内部类(内部定义普通类、抽象类、接口的统称)是指一种嵌套的结构…

    2020年12月14日
    1.3K
  • Java自学之I/O编程

    I/O(Input/Output,输入/输出)可以实现数据的读取与写入操作,Java针对I/O操作的实现提供了java.io工具包,此包的核心组成由File类、InputStrea…

    2020年12月21日
    1.1K