欧博官网手机版:看了Java的Class的源码,我自闭了

2020-06-21 26 views 0

扫一扫用手机浏览

java源码之Class

​ 源码的重要性不言而喻,虽然死板,然则也有拍案叫绝。这是我的源码系列第二弹,后续还会一直更新,迎接交流。String源码可以看我的Java源码之String,若有不足,希望指正。

1.class这个类是什么

Class的本质也是一个类,只不过它是将我们界说类的配合的部门举行抽象,好比我们常界说的类都含有组织方式,类变量,函数,而Class这个类就是来操作这些属性和方式的。固然我们常界说的类包罗的类型都可以通过Class间接的来操作。而类的类型包罗一样平常的类,接口,枚举类型,注解类型等等。这么说可能有点太理论,我们看下面这个例子:

我们将生涯中的一类事物抽象为一个类的时刻,往往是由于他们具有相同的共性和差别的个性。界说一个类的作用就是将相同的共性抽离出来。一样平常的类都包罗属性和方式(行为),下面我们界说水果和汽车这两个大类:

代码如下:

汽车类:

class Car{

    // 界说属性
    private String name;
    private String color;

    /**
     * 界说两个组织方式
     */
    public Car(){

    }

    public Car(String name,String color){
        this.name = name;
        this.color = color;
    }

    /**
     * 界说两个通俗方式(行为)
     */
    public void use(){
        
    }
    
    public void run(){
        
    }

    /**
     * 属性的get和set方式
     * @return
     */
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
         this.color = color;
    }
}


水果类:

class Fruit{

    // 界说属性
    private String name;
    private int size;

    /**
     * 界说两个组织方式
     */
    public Fruit(){

    }

    public Fruit(String name,int size){
        this.name = name;
        this.size =size;
    }

    /**
     * 界说两个方式(行为)
     */
    public void use(){
        
    }
    
    public void doFruit(){
        
    }

    /**
     * 属性的get和set方式
     * @return
     */
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }
}

可以看到水果和汽车这两个类都有配合的部门,也就是一个类配合的部门,那就是属性和方式,而Class就是来操作我们界说类的属性和方式。

sunbet  第1张

​小试牛刀:通过Class这个类来获取Fruit这个类中界说的方式;

public static void main(String[] args) {

        Fruit fruit = new Fruit();
        Class fruitClass = fruit.getClass();

        Method[] fruitMethods = fruitClass.getMethods();
        System.out.println("方式个数:" + fruitMethods.length);

        for (Method method : fruitMethods) {
            //获得返回类型
            System.out.print("方式名称和参数:" + method.getName() + "(");
            //取得某个方式对应的参数类型数组
            Class[] paramsType = method.getParameterTypes();
            for (Class paramType : paramsType) {
                System.out.print(paramType.getTypeName() + " ");
            }
            System.out.print(")");

            Class returnType = method.getReturnType();
            System.out.println("返回类型:" + returnType.getTypeName());
        }
    }

运行效果:

方式个数:15
方式名称和参数:getName()返回类型:java.lang.String
方式名称和参数:setName(java.lang.String )返回类型:void
方式名称和参数:getSize()返回类型:int
方式名称和参数:setSize(int )返回类型:void
方式名称和参数:use()返回类型:void
方式名称和参数:doFruit()返回类型:void
方式名称和参数:wait()返回类型:void
方式名称和参数:wait(long int )返回类型:void
方式名称和参数:wait(long )返回类型:void
方式名称和参数:equals(java.lang.Object )返回类型:boolean
方式名称和参数:toString()返回类型:java.lang.String
方式名称和参数:hashCode()返回类型:int
方式名称和参数:getClass()返回类型:java.lang.Class
方式名称和参数:notify()返回类型:void
方式名称和参数:notifyAll()返回类型:void

这里可能有人疑惑了,Fruit类并没有界说的方式为什么会泛起,如wait(),equals()方式等。这里就有必要说一下java的继续和反射机制。在继续时,java划定每个类默认继续Object这个类,上述这些并没有在Fruit中界说的方式,都是Object中的方式,我们看一下Object这个类的源码就会一清二楚:

 public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

 public final native void wait(long timeout) throws InterruptedException;

 public final void wait() throws InterruptedException {
        wait(0);
    }

而Class类中的getMethods()方式默认会获取父类中的公有方式,也就是public修饰的方式。以是Object中的公共方式也泛起了。

注: 要想获得父类的所有方式(public、protected、default、private),可以使用apache commons包下的FieldUtils.getAllFields()可以获取类和父类的所有(public、protected、default、private)属性。

是不是感受异常的壮大 ,固然,使用Class来获取一些类的方式和属性的焦点头脑就是行使了Java反射特征。万物皆反射,可见反射的壮大之处,至于反射的原理,期待我的下一个博客。

2.常用方式的使用以及源码剖析

2.1组织方式

源码如下:

 private Class(ClassLoader loader) {
        // Initialize final field for classLoader.  The initialization value of non-null
        // prevents future JIT optimizations from assuming this final field is null.
        classLoader = loader;
    }

可以看到Class类只有一个组织函数,而且是私有的。也就是说不能通过new来建立这个类的实例。官方文档的注释:私有组织函数,仅Java虚拟机建立Class工具。我想可能就是为了平安,详细缘故原由不是很领会。若是有领会的话,可以在谈论区内配合的交流。

Class是怎么获取一个实例的。

那么既然这个class组织器私有化,那我们该若何去组织一个class实例呢,一样平常接纳下面三种方式:

1.运用.class的方式来获取Class实例。对于基本数据类型的封装类,还可以接纳.TYPE来获取相对应的基本数据类型的Class实例,如下的示例。

 // 通俗类获取Class的实例。接口,枚举,注解,都可以通过这样的方式举行获得Class实例
Class fruitClass = Fruit.class;

// 基本类型和封装类型获得Class实例的方式,两者等效的
Class intClass = int.class;
Class intClass1 = Integer.TYPE;

下面的表格双方等价:

boolean.class Boolean.TYPE
char.class Character.TYPE
byte.class Byte.TYPE
short.class Short.TYPE
int.class Integer.TYPE
long.class Long.TYPE
float.class Float.TYPE
double.class Double.TYPE
void.class Void.TYPE

然则这种方式有一个不足就是对于未知的类,或者说不能见的类是不能获取到其Class工具的。

2.行使工具.getClass()方式获取该工具的Class实例;

这是行使了Object提供的一个方式getClass() 来获取当着实例的Class工具,这种方式是开发中用的最多的方式,同样,它也不能获取到未知的类,好比说某个接口的实现类的Class工具。

Object类中的getClass()的源码如下:

public final native Class<?> getClass();

源码说明:

可以看到,这是一个native方式(一个Native Method就是一个java挪用非java代码的接口),而且不允许子类重写,以是理论上所有类型的实例都具有同一个 getClass 方式。

使用:

 Fruit fruit = new Fruit();
 Class fruitClass = fruit.getClass();

3.使用Class类的静态方式forName(),用类的名字获取一个Class实例(static Class forName(String className) ),这种方式灵活性最高,凭据类的字符串全名即可获取Class实例,可以动态加载类,框架设计经常用到;

源码如下:

    /*
     由于方式区 Class 类型信息由类加载器和类全限命名唯一确定,以是参数name必须是全限命名,
     参数说明   name:class名,initialize是否加载static块,loader 类加载器
     */
    public static Class<?> forName(String name, boolean initialize,
                                   ClassLoader loader)
        throws ClassNotFoundException
    {
        Class<?> caller = null;
        
        // 1.举行平安检查
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
           ....
            }
        }
        // 2.挪用内陆的方式
        return forName0(name, initialize, loader, caller);
    }
   
    // 3.焦点的方式
    private static native Class<?> forName0(String name, boolean initialize,
                                            ClassLoader loader,
                                            Class<?> caller)
      throws ClassNotFoundException;

   /* 
    这个 forName是上述方式的重载,平时一样平常都使用这个 方式默认使用挪用者的类加载器,将类的.class文件加载     到 jvm中
    这里传入的initialize为true,会去执行类中的static块
    */
    public static Class<?> forName(String className)
                throws ClassNotFoundException {
        Class<?> caller = Reflection.getCallerClass();
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }

源码说明已在注释中说明,有些人会疑惑, static native Class<?> forName0()这个方式的实现。

这就要说到java的不完美的地方了,Java的不足除了体现在运行速度上要比传统的C++慢许多之外,Java无法直接接见到操作系统底层(如系统硬件等),为此Java使用native方式来扩展Java程序的功效。有关native的方式请移步这里。

基本使用:

 Class fruitClass = Class.forName("cn.chen.test.util.lang.Fruit");

: 这种方式必须使用类的全限命名,,这是由于由于方式区 Class 类型信息由类加载器和类全限命名唯一确定,否则会抛出ClassNotFoundException的异常。

2.2一样平常方式以及源码剖析:

Class类的一样平常的方式总共有六十多种,实在看到这么多方式咱也不要怂,这内里另有许多重载的方式,凭据二八原则,我们平时用的也就那么几个方式,以是这里只对以下几个方式的使用和实现举行交流,其他的方式可以移步Java官方文档:

sunbet  第2张

2.2.1 获得类的组织方式

这个方式主要是用来领会一个类的组织方式有哪些,包罗那些参数,特别是在单例的模式下。一样平常包罗的方式如下:

  • public Constructor[] getConstructors() :获取类工具的所有可见的组织函数

  • public Constructor[] getDeclaredConstructors():获取类工具的所有的组织函数

  • public Constructor getConstructor(Class... parameterTypes): 获取指定的可见的组织函数,参数为:指定组织函数的参数类型数组,若是该组织函数不能见或不存在,会抛出 NoSuchMethodException 异常

  • public Constructor getDeclaredConstructor(Class... parameterTypes) :获取指定的组织函数,参数为:指定组织函数的参数类型数组,无论组织函数可见性若何,均可获取

基本使用:

Constructor[] constructors = fruitClass.getConstructors();
 for (Constructor constructor : constructors) {
            System.out.println("获得共有的组织方式:"+constructor);
        }

输出效果:

获得共有的组织方式:public cn.chen.test.util.lang.Fruit()
获得共有的组织方式:public cn.chen.test.util.lang.Fruit(java.lang.String,int)

可以看到我们前面界说的来个组织方式,都被打印出来了。注重getConstructors()只能获得被public修饰的组织方式,若是要获得被(protected,default,private)修饰的组织方式,就要使用的getDeclaredConstructors()这个方式了。接下来,修改Fruit中的一个组织方式为private:

 private  Fruit(String name,int size){
        this.name = name;
        this.size =size;
    }

使用getConstructors()和getDeclaredConstructors()着两个方式举行测试:

       Class fruitClass = Fruit.class;       
       Constructor[] constructors = fruitClass.getConstructors();
       Constructor[] constructors1 = fruitClass.getDeclaredConstructors();

        for (Constructor constructor : constructors) {
            System.out.println("获得共有的组织方式:"+constructor);
        }

        System.out.println("=================================================");
        for (Constructor constructor : constructors1) {
            System.out.println("获得所有的组织方式:"+constructor);
        }

输出效果:

获得共有的组织方式:public cn.chen.test.util.lang.Fruit()
===================分隔线=============================
获得所有的组织方式:public cn.chen.test.util.lang.Fruit()
获得所有的组织方式:private cn.chen.test.util.lang.Fruit(java.lang.String,int)

可以看到两者的区别。以是,反射在一定程度上破坏了java的封装特征。究竟人无完人,语言亦是一样。

getConstructors()的源码剖析:

public Constructor<?>[] getConstructors() throws SecurityException {
          
        // 1.检查是否允许接见。若是接见被拒绝,则抛出SecurityException。
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        return copyConstructors(privateGetDeclaredConstructors(true));
    }
    
 private static <U> Constructor<U>[] copyConstructors(Constructor<U>[] arg) {
      // 2.使用克隆,获得当前类的所有组织函数   
      Constructor<U>[] out = arg.clone();
     // 3.使用ReflectionFactory组织一个工具,也是不使用组织方式组织工具的一种方式。
        ReflectionFactory fact = getReflectionFactory();
     // 4.遍历,将组织函数举行拷贝返回,注重在挪用fact.copyConstructor(out[i])这个方式的时刻,还会举行平安检查,用的就是下面的LangReflectAccess() 这个方式。
        for (int i = 0; i < out.length; i++) {
            out[i] = fact.copyConstructor(out[i]);
        }
        return out;
    }



 private static LangReflectAccess langReflectAccess() {
        if (langReflectAccess == null) {
            Modifier.isPublic(1);
        }

        return langReflectAccess;
    } 

通过打断点调试,可以看到下面的信息:

sunbet  第3张

代码的挪用逻辑在注释里已举行说明。

2.2.2 获得属性

主要获取类的属性字段,领会这个类声明晰那些字段。

一样平常有四个方式:

  • public Field[] getFields():获取所有可见的字段信息,Field数组为类中声明的每一个字段保留一个Field 实例
  • public Field[] getDeclaredFields():获取所有的字段信息
  • public Field getField(String name) :通过字段名称获取字符信息,该字段必须可见,否则抛出异常
  • public Field getDeclaredField(String name) :通过字段名称获取可见的字符信息

基本使用:

首先我们在Fruit的类中加入一个public修饰的属性:

    public double weight;
Class fruitClass = Fruit.class; 
Field[] field2 = fruitClass.getFields();
        for (Field field : field2) {
            System.out.println("界说的公有属性:"+field);
        }

        Field[] fields = fruitClass.getDeclaredFields();
        for (Field field : fields) {
            System.out.println("界说的所有属性:"+field);
        }

输出效果:

界说的公有属性:public double cn.chen.test.util.lang.Fruit.weight
========================分隔线============================
界说的所有属性:private java.lang.String cn.chen.test.util.lang.Fruit.name
界说的所有属性:private int cn.chen.test.util.lang.Fruit.size
界说的所有属性:public double cn.chen.test.util.lang.Fruit.weight

源码剖析,就以getFileds()这个方式为例,涉及以下几个方式:

public Field[] getFields() throws SecurityException {
        // 1.检查是否允许接见。若是接见被拒绝,则抛出SecurityException。
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        return copyFields(privateGetPublicFields(null));
    }
 
 private static Field[] copyFields(Field[] arg) {
         // 2. 声明一个Filed的数组,用来存储类的字段 
        Field[] out = new Field[arg.length];
        //  3.使用ReflectionFactory组织一个工具,也是不使用组织方式组织工具的一种方式。
        ReflectionFactory fact = getReflectionFactory();
       // 4.遍历,将字段复制后返回。
        for (int i = 0; i < arg.length; i++) {
            out[i] = fact.copyField(arg[i]);
        }
        return out;
    }
    
 public Field copyField(Field var1) {
        return langReflectAccess().copyField(var1);
    }
 
// 再次检查属性的接见权限
  private static LangReflectAccess langReflectAccess() {
        if (langReflectAccess == null) {
            Modifier.isPublic(1);
        }

        return langReflectAccess;
    }

2.2.3 获得一样平常方式

就是获取一个类中的方式,一样平常有以下几个方式:

  • public Method[] getMethods(): 获取所有可见的方式

  • public Method[] getDeclaredMethods() :获取所有的方式,无论是否可见

  • public Method getMethod(String name, Class... parameterTypes)

    参数说明:

  1. 通过方式名称、参数类型获取方式
  2. 若是你想接见的方式不能见,会抛出异常
  3. 若是你想接见的方式没有参数,通报 null作为参数类型数组,或者不传值)
  • public Method getDeclaredMethod(String name, Class... parameterTypes)
  1. 通过方式名称、参数类型获取方式
  2. 若是你想接见的方式没有参数,通报 null作为参数类型数组,或者不传值)

基本使用:

//在fruit中界说一个这样的方式
 private  void eat(String describe){
        System.out.println("通过getMethod()方式挪用了eat()方式:  "+describe);
    }

挪用这个方式:

        Class fruitClass = Fruit.class;
        Method method = fruitClass.getDeclaredMethod("eat",String.class);
        method.setAccessible(true);
        method.invoke(fruitClass.newInstance(),"我是该方式的参数值");

输出效果:

  通过getMethod()方式挪用了eat()方式:我是该方式的参数值

剖析getDeclaredMethod()涉及的源码:

public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        // 1.检查方式的修饰符
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        // 2.searchMethods()方式的第一个参数确定这个方式是不是私有方式,第二个参数我们界说的方式名,第三个参数就是传入的方式的参数类型
        Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
        if (method == null) {
            throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
        }
        return method;
    }

// 这个方式就是通过传入的方式名找到我们界说的方式,然后使用了Method的copy()方式返回一个Method的实例,我们通过操作mehtod这个实例就可以操作我们界说的方式。
 private static Method searchMethods(Method[] methods,
                                        String name,
                                        Class<?>[] parameterTypes)
    {
        Method res = null;
        String internedName = name.intern();
        for (int i = 0; i < methods.length; i++) {
            Method m = methods[i];
            if (m.getName() == internedName
                && arrayContentsEq(parameterTypes, m.getParameterTypes())
                && (res == null
                    || res.getReturnType().isAssignableFrom(m.getReturnType())))
                res = m;
        }

        return (res == null ? res : getReflectionFactory().copyMethod(res));
    }

 public Method copyMethod(Method var1) {
        return langReflectAccess().copyMethod(var1);
    }

 
// 检查属性的接见权限
  private static LangReflectAccess langReflectAccess() {
        if (langReflectAccess == null) {
            Modifier.isPublic(1);
        }

        return langReflectAccess;
    }

2.2.4 判断类的类型的方式

这类型的方式顾名思义,就是来判断这个类是什么类型,是接口,注解,枚举,照样一样平常的类等等。部门方式如下表

boolean isAnnotation()判断是不是注解
boolean isArray() 判断是否为数组
boolean isEnum()判断是否为枚举类型
boolean isInterface() 是否为接口类型
boolean isMemberClass()当且仅当基础类是成员类时,返回“true”
boolean isPrimitive()确定指定的“类”工具是否示意原始类型。
boolean isSynthetic()若是这个类是合成类,则返回' true ';否则返回“false”。

基本用法:

// 界说一个接口:
interface  Animal{
    public void run();
}

判断是不是一个接口:

Class AnimalClass = Animal.class;
 boolean flag = AnimalClass.isInterface();
 System.out.println(flag);

输出效果:

true

源码剖析isInterface():

 public native boolean isInterface();

这是一个native方式,人人都知道native方式是非Java语言实现的代码,供Java程序挪用的,由于Java程序是运行在JVM虚拟机上面的,要想接见到对照底层的与操作系统相关的就没办法了,只能由靠近操作系统的语言来实现。

2.2.5 toString()方式

将工具转换为字符串。字符串示意形式是字符串“类”或“接口”,后跟一个空格,然后是该类的全限命名。

基本使用:

// 这是前面界说的两个类Fruit和Car,Car是一个接口
 Class fruitClass = Fruit.class;
 Class AnimalClass = Animal.class;
 System.out.println(AnimalClass.toString());
 System.out.println(fruitClass.toString());

输出效果:

// 花样  字符串“类”或“接口”,后跟一个空格,然后是该类的全限命名
interface cn.chen.test.util.lang.Animal
class cn.chen.test.util.lang.Fruit

源码如下:

 public String toString() {
       // 先是判断是接口或者类,然后挪用getName输出类的全限命名
        return (isInterface() ? "interface " : (isPrimitive() ? "" : "class "))
            + getName();
    }

  public native boolean isInterface();
  public native boolean isPrimitive();

追本溯源,方能阔步前行。

参考资料

​ https://blog.csdn.net/x_panda/article/details/17120479

​ https://juejin.im/post/5d4450fbe51d4561ce5a1be1

​ JavaSE的官方文档

,

欧博亚洲电脑版下载

欢迎进入欧博亚洲电脑版下载(Allbet Game):www.aLLbetgame.us,欧博官网是欧博集团的官方网站。欧博官网开放Allbet注册、Allbe代理、Allbet电脑客户端、Allbet手机版下载等业务。

Sunbet内容转载自互联网,如有侵权,联系Sunbet删除。

本文链接地址:http://www.18hao-soso.com/post/1384.html

相关文章