谈谈Java中的反射与Class等类

反射与Class类

(所有源代码已经上传到码云上:https://gitee.com/c3h2/knowledge-study.git 该章节所属reflect包中,具体代码开编号,每个类都有编号)

1.1 反射简介

在Java之中,反射就是其中最重要的特性之一,也正是因为Java支持反射,才使得Java的设计更加灵活,这么多年也一直经久不衰的原因之一(抗高并发也是一个原因)。如果你不掌握反射,那么在Java里面很难走的通,也写不出高复用的代码,因为几乎所有的框架都是基于反射来实现的。

既然Java反射如此的重要,那么什么是Java反射呢,Java反射中的“反”又是什么意思呢?观察如下代码(源码reflect.cn.tansanqinger.basics.Test1.java):

public class Test1 {
    public static void main(String[] args) {
        Class<?> clazz = new java.util.Date().getClass();
        System.out.println(clazz);//直接输出,详细描述所有信息
        System.out.println(clazz.getName());//获取类完整的包名信息
        System.out.println(clazz.getSimpleName());//获取类名
    }
}

上述代码运行结果为

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bqi8pmfK-1600683885467)(C:\Users\C3H2\AppData\Roaming\Typora\typora-user-images\image-20200921113201484.png)]

上述代码获取了Date类的相关信息,包括类路径和类名,相当于是通过一个名称,将它的底完全挖掘出来了。能够获取到类的包名,那么我们就可以实现不导包而将类进行实例化操作,这样就降低了耦合。

1.2 获取Class类对象实例

在Java反射中用于获取Class类对象实例的方式有三,他们分别是:

  • 第一种:利用Object类提供的getClass()方法,通过实例化对象调用获得

  • 第二种:利用“类.class”的原生方式来获得

  • 第三种:通过Class类中提供的方法进行实例化

上述获取实例的new java.util.Date().getClass() 也可以修改为java.util.Date.class,前者利用Object中的getClass()方法来获取Class类的实例,实际上需要对通过指定类的实例化对象来完成,但是后者可以在没有指定类实例化对象的时候直接获取Class实例,相比之前节约了一个实例化对象。

除此之外,还有Class内部提供的一个forName方法,利用该方法也可以实现Class的实例化,该方法的定义为:

public static Class<?> forName(String className) throws ClassNotFoundException

使用此方法需要注意的是,要书写正确的类路径和类名,否则将会抛出ClassNotFoundException这个异常。

三种方式都很重要,因此都需要掌握

  • getClass():需要通过明确的获得使用类的实例化对象;

  • 类.class:需要明确的进行操作类的导入处理;

  • Class.forName():可以通过字符串描述要使用类的名称;

三种不同方式在开发中的使用如下(源码reflect.cn.tansanqinger.basics.Test2.java):

public class Test2 {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> clsA = java.util.Date.class;
        Class<?> clsB = Class.forName("java.util.Date");
        Class<?> clsC = new java.util.Date().getClass();
        System.out.println(clsA);
        System.out.println(clsB);
        System.out.println(clsC);
        System.out.println(clsA.getName());
        System.out.println(clsB.getName());
        System.out.println(clsC.getName());
        System.out.println(clsA.getSimpleName());
        System.out.println(clsB.getSimpleName());
        System.out.println(clsC.getSimpleName());
    }
}

运行结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b41DzvtN-1600683885474)(C:\Users\C3H2\AppData\Roaming\Typora\typora-user-images\image-20200921132410798.png)]

1.3 反射与类操作

上述说了Java反射的三种使用方式,这一节来分析一下反射与类的操作,首先看一组方法

序号方法名称类型说明
1public Package getPackage()方法获取程序所在的包名称
2public Class<? super T> getSuperclass()方法获取所继承的父类
3public Class<?>[] getInterfaces()方法获取所有的父接口
4public Constructor **getConstructor(Class<?>… parameterTypes) ** throws NoSuchMethodException, SecurityException方法根据指定的参数类型获取指定的构造方法(public)
5public Class<?>[] getConstructors() throws SecurityException方法获取类中的所有的构造方法(public)
6public Constructor getDeclaredConstructor(Class<?>… parameterTypes) throws NoSuchMethodException, SecurityException方法获取一个类中指定的构造方法(public、protected、default)
7public Constructor<?>[] **getDeclaredConstructors() **throws SecurityException方法获取一个类中的全部构造方法(public、protected、default)
8public Method[] getDeclaredMethods() throws SecurityException方法获取本类中的所有方法
9public Method getDeclaredMethod(String name, Class<?>… parameterTypes) throws NoSuchMethodException, SecurityException方法获取本类一个指定类型的Method实例
10public Method getMethod(String name, Class<?>… parameterTypes) throws NoSuchMethodException, SecurityException方法获取一个公共的方法,包括父类的方法
11public Field[] getFields() throws SecurityException方法获取所有继承来的public成员
12public Field getField(String name) throws NoSuchFieldException, SecurityException方法获取一个指定类型的成员
13public Field[] getDeclaredFields() throws SecurityException方法获取本类定义的全部的成员
14public Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException方法获取一个本类定义成员
15public Class<?> getType()获取属性类型
16public Object get(Object obj) throws IllegalArgumentException, IllegalAccessException获取属性内容
17public void set(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException设置属性内容
18public void setAccessible(boolean flag)取消封装

上述的所有的方法就是Java反射中重要的方法,当然还有很多的方法,这里就不一一列举了,如果你有兴趣,可以直接去查看源代码JDK12文档了解更多。

同时需要注意的一件事:使用反射进行类实例化在jdk1.9之后发生了变化。我们可以通过官网的源代码来了解这一变化。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gLJt8oyo-1600683885477)(C:\Users\C3H2\AppData\Roaming\Typora\typora-user-images\image-20200921172320288.png)]

下面演示一下这些方法的使用(源代码reflect.cn.tansanqinger.basics.Test3.java):

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
interface IMessage{}
interface IChannel{}
abstract class AbstraceChannalMessage implements IMessage,IChannel{
    public void AbstractChannelMessage() {}
    public void AbstractChannelMessage(String username, String password) {}
}
//该类为定义方法,但是可以获取继承来的方法
class Person{}
class CloudMessage extends AbstraceChannalMessage implements IMessage,IChannel{
    public CloudMessage() {}
    public CloudMessage(String url, String userName, String password) {}
}
public class Test3 {
    public static void main(String[] args) {
        Class<?> clazzA = CloudMessage.class;
        System.out.println("【获取包的名称】"+clazzA.getPackage().getName());
        System.out.println("【获取继承父类】"+clazzA.getSuperclass().getName());
        System.out.println("【获取实现接口】"+ Arrays.toString(clazzA.getInterfaces()));
        //---------------获取构造方法----------------
        Class<?> clazzB = CloudMessage.class;
        Constructor<?> constructor[] = clazzB.getDeclaredConstructors();
        for(Constructor<?> cont : constructor) {
            System.out.println(cont);
        }
        //-------------获取方法--------------
        Class<?> clazz = Person.class;
        Method methods[] = clazz.getMethods();
        for(int i = 0 ; i < methods.length ; i ++) {
            System.out.print(Modifier.toString(methods[i].getModifiers())+" ");
            System.out.print(methods[i].getReturnType().getName()+" ");
            System.out.print(methods[i].getName()+"(");
            Class<?> params[] = methods[i].getParameterTypes();//获取所有参数
            for(int x = 0 ; x < params.length ; x++) {
                System.out.print(params[x].getSimpleName()+" arg-"+x);
                if(x<params.length -1) {
                    System.out.print(",");
                }
            }
            System.out.print(") ");
            Class<?> exps[] = methods[i].getExceptionTypes();
            if(exps.length > 0) {
                System.out.print("throws ");
                for(int x = 0 ; x < exps.length ; x++) {
                    System.out.print(exps[x].getSimpleName());
                    if(x <params.length -1) {
                        System.out.print(",");
                    }
                }
            }
        System.out.println();
        }
    }
}

剩下几个类似的使用方法,这里就不细说了。

1.4Unsafe工具类

为了进一步对反射进行扩展,Java提供了一个Unsafe类,该类位于sun.misc包之中,该类主要的功能就是通过反射来获取对象,打破JVM的固定对象使用模式,该类直接通过C++对数据进行读取,这个类可以在没有实例化对象的时候进行类中方法的调用。

构造方法:private Unsafe() {}

常量:private static final Unsafe theUnsafe = new Unsafe();

由于构造方法被私有化了,因此该类需要通过内部定义的“theUnsafe”常量对象来进行Unsafe类的调用。使用方法如下(源代码reflect.cn.tansanqinger.basics.Test4.java):

class Singleton{
    private Singleton(){
        System.out.println("构造方法被私有化");
    }
    public void msg(){
        System.out.println("www.tansanqinger.cn");
    }
}
public class Test4 {
    public static void main(String[] args) {
        try{
            Field unsafe = Unsafe.class.getDeclaredField("theUnsafe");
            unsafe.setAccessible(true);
            Unsafe unsafe1 = (Unsafe) unsafe.get(null);
            Singleton singleton = (Singleton) unsafe1.allocateInstance(Singleton.class);
            singleton.msg();
        } catch (Exception e){}
    }
}

现在可以发现利用Unsafe类可以打破已有的JVM中关于对象的使用的模型,可以在没有实例化对象的时候直接调用类中所提供的普通方法,但是这样的操作表示的处理全部由程序自己完成,而所有的代码不受到JVM的控制,也就意味着所有的垃圾回收机制就将失效。
提出意义:Unsafe只是提供了一种底层的直接进行对象操作的支持,与实际的开发意义不大。如果在以后笔试的过程里面要求你编写单例设计模式的时候,请你注意如下几点:要使用懒汉式单例设计、要针对于数据的同步做出处理、补充上利用Unsafe可以绕过对象的实例化机制,直接调用类中的方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值