5

动态加载不适合数组类?那如何动态加载一个数组类?

 3 years ago
source link: https://my.oschina.net/u/4526289/blog/5440164
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

摘要:既然数组是一个类,那么编译后类名是什么?类路径呢?为什么说动态加载不适合数组?那应该如何动态加载一个数组?

本文分享自华为云社区《【JAVA冷知识】动态加载不适合数组类?那如何动态加载一个数组类?》,作者:山河已无恙。

  • 今天和小伙伴分享一些java小知识点,主要围绕下面几点:
  • 既然数组是一个类,
  • 那么编译后类名是什么?类路径呢?
  • 为什么说动态加载不适合数组?
  • 那应该如何动态加载一个数组?
  • 部分内容参考
    • 《编写高质量代码(改善Java程序的151个建议)》
    • 《深入理解Java虚拟机》

一、既然数组是一个类,那么编译后类名是什么?

通下面的代码我们可以看出,对于基本类型数组,编译后为[+基本类型标识,对于引用类型为[L+引用类类路径

package com.liruilong;

import java.util.logging.Logger;

/**
 * @Project_name: workspack
 * @Package: com.liruilong
 * @Description:
 * @Author: [email protected]
 * @WeChat_Official_Accounts: 山河已无恙
 * @blog: https://liruilong.blog.csdn.net/
 * @Date: 2022/2/9  3:08
 */
public class ArrayDemo {
    static Logger logger = Logger.getAnonymousLogger();

    public static void main(String[] args) {
        logger.info("基本类型数组编译后类名:" + int[].class.getName());
        logger.info("引用类型数组编译后类名:" + String[].class.getName());

    }

}
二月 09, 2022 3:57:03 上午 com.liruilong.ArrayDemo main
信息: 基本类型数组编译后类名:[I
二月 09, 2022 3:57:03 上午 com.liruilong.ArrayDemo main
信息: 引用类型数组编译后类名:[Ljava.lang.String;

Process finished with exit code 0

在java中数组是一个较为特殊的类,不管是基本类型数组,还是引用类型数组,都没有可追溯的类路径

数组元素类型及编译后的类型

v2-014c53514b26c1314b559c1235b769c6_720w.jpg

二、为什么动态加载不适合数组

关于动态加载,这里不多讲,相信小伙伴么都不陌生,在原始的JDBC编程连接数据库的时候,通常会通过静态块动态的加载一个连接数据库的驱动类,这里会用到Class.forName(driver),将驱动类加载到内存中。

当然这里forName只是把一个类加载到内存中,并不是产生一个实例对象,也不会执行任何方法,具体的注入的驱动类如何生成对象,如何注册到DriverManager,一般可以通过静态块的方式实现,即类加载的同时生成实例对象并注册。

我们知道在类加载(加载,验证,准备,解析,初始化)的最后一步类初始化的时候,执行类构造器<clinit>()方法,<clinit>()方法是编译器自动收集类中的所有类变量的赋值动作的和静态语句块的中的语句合并产生的。编译器收集的顺序是由语句中源文件中出现的顺序决定。

下面是mysql驱动类的源码

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.mysql.jdbc;

import java.sql.DriverManager;
import java.sql.SQLException;

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }

    static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

为什么不适合数组

关于动态加载,小伙伴可以看看《深入理解Java虚拟机》,回到我们的问题,为什么数组不适合动态加载,由上面的代码可以知道,当使用forName加载一个类时,需要一个类的全路径,或者说全限定名。

但是不管是基本类型数组,还是引用类型数组,都没有可追溯的类路径,不是一个具体的类,所以在加载的时候,会报错java.lang.ClassNotFoundException

package com.liruilong;
import java.util.logging.Logger;

/**
 * @Project_name: workspack
 * @Package: com.liruilong
 * @Description:
 * @Author: [email protected]
 * @WeChat_Official_Accounts: 山河已无恙
 * @blog: https://liruilong.blog.csdn.net/
 * @Date: 2022/2/9  3:08
 */
public class ArrayDemo {
    static Logger logger = Logger.getAnonymousLogger();

    public static void main(String[] args) throws ClassNotFoundException {
        Class.forName("java.lang.String[]");
        Class.forName("int[]");
    }

}

Exception in thread "main" java.lang.ClassNotFoundException: java/lang/String[]
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:264)
	at com.liruilong.ArrayDemo.main(ArrayDemo.java:19)

Process finished with exit code 1

直接加载不可以,那么加载一个数组编译后的类型是否可行呢?我们来看看

package com.liruilong;
import java.util.logging.Logger;

/**
 * @Project_name: workspack
 * @Package: com.liruilong
 * @Description:
 * @Author: [email protected]
 * @WeChat_Official_Accounts: 山河已无恙
 * @blog: https://liruilong.blog.csdn.net/
 * @Date: 2022/2/9  3:08
 */
public class ArrayDemo {
    static Logger logger = Logger.getAnonymousLogger();

    public static void main(String[] args) throws ClassNotFoundException {
        Class.forName("[Ljava.lang.String;");
        Class.forName("[I");
    }

}

Bad level value for property: .level
Bad level value for property: java.util.logging.ConsoleHandler.level

Process finished with exit code 0

通过上面我们可以知道,可以加载编译后的类路径动态加载一个对象数组,但是没有意义。并不能通过newInstance()方法生成一个实例对象,在java中数组是定长的,没有长度的数组是不允许存在的。

package com.liruilong;
import java.util.logging.Logger;

/**
 * @Project_name: workspack
 * @Package: com.liruilong
 * @Description:
 * @Author: [email protected]
 * @WeChat_Official_Accounts: 山河已无恙
 * @blog: https://liruilong.blog.csdn.net/
 * @Date: 2022/2/9  3:08
 */
public class ArrayDemo {
    static Logger logger = Logger.getAnonymousLogger();

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Class<String[]> aClass = (Class<String[]>) Class.forName("[Ljava.lang.String;");
        String[] strings = aClass.newInstance();
    }

}

Bad level value for property: .level
Bad level value for property: java.util.logging.ConsoleHandler.level
Exception in thread "main" java.lang.InstantiationException: [Ljava.lang.String;
	at java.lang.Class.newInstance(Class.java:427)
	at com.liruilong.ArrayDemo.main(ArrayDemo.java:20)
Caused by: java.lang.NoSuchMethodException: [Ljava.lang.String;.<init>()
	at java.lang.Class.getConstructor0(Class.java:3082)
	at java.lang.Class.newInstance(Class.java:412)
	... 1 more

Process finished with exit code 1

三、如何动态加载一个数组

那如何通过类似动态加载的方式生成一个数组,我们可以使用Array数组工具类来动态加载一个数组。

package com.liruilong;
import java.lang.reflect.Array;
import java.util.logging.Logger;

/**
 * @Project_name: workspack
 * @Package: com.liruilong
 * @Description:
 * @Author: [email protected]
 * @WeChat_Official_Accounts: 山河已无恙
 * @blog: https://liruilong.blog.csdn.net/
 * @Date: 2022/2/9  3:08
 */
public class ArrayDemo {
    static Logger logger = Logger.getAnonymousLogger();

    public static void main(String[] args)  {
        String [] strings = (String[]) Array.newInstance(String.class,6);
        logger.info("String数组长度:"+strings.length);
        int[][] ints = (int [][])Array.newInstance(int.class,6,3);
        logger.info("int数组长度:"+ints.length);
    }

}
Bad level value for property: .level
Bad level value for property: java.util.logging.ConsoleHandler.level
Can't set level for java.util.logging.ConsoleHandler
二月 09, 2022 5:15:12 上午 com.liruilong.ArrayDemo main
信息: String数组长度:6
二月 09, 2022 5:15:12 上午 com.liruilong.ArrayDemo main
信息: int数组长度:6

Process finished with exit code 0

看看源码,我们会发现这是一个本地方法,通过C或者C++之类的语言实现的

 public static Object newInstance(Class<?> componentType, int length)
        throws NegativeArraySizeException {
        return newArray(componentType, length);
    }
private static native Object newArray(Class<?> componentType, int length)
        throws NegativeArraySizeException;

关于数组的动态加载和小伙伴们分享到这里,生活加油哦 ^_^

点击关注,第一时间了解华为云新鲜技术~


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK