尚硅谷2020最新版宋红康JVM教程-中篇-第4章:再谈类的加载器-04-ClassLoader源码解析

Java类加载一:Class.forName()和ClassLoader.getSystemClassLoader().loadClass()区别 要想搞清楚这两个方法的区别,我们需要了解一下Class的加载过程。Class的加载过程分为三步: loading(装载) linking(链接) initializing(初始化) 大家可以通过这篇文章:Java魔法堂:类加载机制入了个门来了解类的详细加载过程。阅读以上文章后,我们一起分析一下两个方法的区别,如有不正之处,欢迎批评指正。Class.forName()Class.forName有两个重 阅读详情

引言

在这里插入图片描述
在这里插入图片描述

除了以上虚拟机自带的加载器外,用户还可以定制自己的类加载器。Java提供了抽象类java.lang.ClassLoader,所有用户自定义的类加载器都应该继承ClassLoader类。

关于Launcher类

在这里插入图片描述

ClassLoader的主要方法

  • 抽象类ClassLoader的主要方法:(内部没有抽象方法)
  • public final ClassLoader getParent()
  • 返回该类加载器的超类加载器
  • public Class<?> loadClass(String name)throws ClassNotFoundException
  • 加载名称为name的类,返回结果为java.lang.Class类的实例。如果找不到类,则返回ClassNotFoundException异常。该方法中的逻辑就是双亲委派模式的实现。
  • 调用了findClass方法
    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) { // 同步锁
            // 首先,在缓存中判断是否已经加载同名的类
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    // 获取当前类加载器的父类加载器
                    if (parent != null) {
                        // 如果存在父类加载器,则调用父类的loadclass(也是该方法,递归)
                        c = parent.loadClass(name, false);
                    } else {
                        // 如果为null,即是当前类的父类加载器是引导类加载器
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) { // 当前类的加载器的父类加载器未加载此类 or 当前类的加载器未加载此类
                    // 调用当前ClassLoader的findClass()
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) { // 是否进行解析
                resolveClass(c);
            }
            return c;
        }
  • protected Class<?> findClass(String name) throws ClassNotFoundException
  • 查找二进制名称为name的类,返回结果为java.lang.Class类的实例。这是一个受保护的方法,JVM鼓励我们重写此方法,需要自定义加载器遵循双亲委托机制,该方法会在检查完父类加载器之后被loadClass()方法调用。
  • 调用defineclass方法
  • 在J0Kl.2之前,在自定义类加载时,总会去继承ClassLoader类并重写loadClass方法,从而实现自定义的类加载类。但是在JDKl.2之后已不再建议用户去覆盖loadClass()方法,而是建议把自定义的类加载逻辑写在findClass()方法中,从前面的分析可知,findClass()方法是在loadClass()方法中被调用的,当loadClass()方法中父加载器加载失败后,则会调用自己的findClass()方法来完成类加载,这样就可以保证自定义的类加载器也符合双亲委托模式。
  • 需要注意的是ClassLoader类中并没有实现findClass()方法的具体代码逻辑,取而代之的是抛出ClassNotFoundException异常,同时应该知道的是findClass方法通常是和defineClass方法一起使用的。一般情况下,在自定义类加载器时,会直接覆盖ClassLoader的findClass()方法并编写加载规则,取得要加载类的字节码后转换成流,然后调用defineClass()方法生成类的Class对象。
  • protected final Class<?>defineclass(String name,byte[]b,int off,int len)
  • 根据给定的字节数组b转换为Class的实例,off和len参数表示实际Class信息在byte数组中的位置和长度,其中byte数组b是ClassLoader从外部获取的。这是受保护的方法,只有在自定义ClassLoader子类中可以使用。
  • defineClass()方法是用来将byte字节流解析成JVM能够识别的Class对象(ClassLoader中已实现该方法逻辑),通过这个方法不仅能够通过class文件实例化class对象,也可以通过其他方式实例化Class对象,如通过网络接收一个类的字节码,然后转换为byte字节流创建对应的Class对象。
  • defineClass()方法通常与findClass()方法一起使用,一般情况下,在自定义类加载器时,会直接覆盖ClassLoader的findClass()方法并编写加载规则,取得要加载类的字节码后转换成流,然后调用defineClass()方法生成类的Class对象
  • 简单举列:
    protected Class<?>findClass(String name)throws ClassNotFoundException{
        //获取类的字黄数组
        byte[]classData=getClassData(name);
        if(classData==null){
            throw new ClassNotFoundException();
        }else{
            //使用defineclass生成class对象
            return defineclass(name,classData,0,classData.length);
        }
    }
  • protected final void resolveClass(Class<?>c)
  • 链接指定的一个Java类。使用该方法可以使用类的Class对象创建完成的同时也被解析。前面我们说链接阶段主要是对字节码进行验证,为类变量分配内存并设置初始值同时将字节码文件中的符号引用转换为直接引用。
  • protected final Class<?>findLoadedClass(String name)
  • 查找名称为name的已经被加载过的类,返回结果为java.lang.Class类的实例。这个方法是final方法,无法被修改。
  • private final ClassLoader parent;
  • 它也是一个ClassLoader的实例,这个字段所表示的ClassLoader也称为这个ClassLoader的双亲。在类加载的过程中,ClassLoader可能会将某些请求交予自己的双亲处理。

SecureClassLoader 与URLClassLoader

接着SecureClassLoader扩展了ClassLoader,新增了几个与使用相关的代码源(对代码源的位置及其证书的验证)和权限定义类验证(主要指对class源码的访问权限)的方法,一般我们不会直接跟这个类打交道,更多是与它的子类URLClassLoader有所关联。

前面说过,ClassLoader是一个抽象类,很多方法是空的没有实现,比如findClass()、findResource()等。而URLClassLoader这个实现类为这些方法提供了具体的实现。并新增了URLClassPath类协助取得Class字节码流等功能
。在编写自定义类加载器时,如果没有太过于复杂的需求,可以直接继承URLClassLoader类,这样就可以避免自己去编写findClass()方法及其获取字节码流的方式,使自定义类加载器编写更加简洁。

在这里插入图片描述

ExtClassLoader 与AppClassLoader

  • 了解完URLClassLoader后接着看看剩余的两个类加载器,即拓展类加载器ExtClassLoader和系统类加载器AppClassLoader,这两个类都继承自URLClassLoader,是sun.misc.Launcher的静态内部类。
    un.misc.Launcher主要被系统用于启动主应用程序,ExtClassLoader和AppClassLoader都是由un.misc.Launcher创建的,其类主要类结构如下:

在这里插入图片描述

在这里插入图片描述

  • 我们发现ExtClassLoader并没有重写loadClass()方法,这足矣说明其遵循双亲委派模式,而AppClassLoader重载了loadClass()方法,但最终调用的还是父类loadClass()方法,因此依然遵守双亲委派模式。

Class.forNam()与ClassLoader.loadClass()

Class.forName()

  • Class.forName():是一个静态方法,最常用的是Class.forName(String className);
  • 根据传入的类的全限定名返回一个Class对象。该方法在将Class文件加载到内存的同时,会执行类的初始化。
  • 如:Class.forName(“com.atguigu.java.Helloworld”);

ClassLoader.loadClass()

  • ClassLoader.loadClass():这是一个实例方法,需要一个ClassLoader对象来调用该方法。
  • 该方法将Class文件加载到内存时,并不会执行类的初始化,直到这个类第一次使用时才进行初始化。该方法因为需要得到一个ClassLoader对象,所以可以根据需要指定使用哪个类加载器。
  • 如:Classloader cl=…;cl.loadClass(“com.atguigu.java.Helloworld”);
超声波清洗机拆解实录:从电路板到换能器,一文看懂内部构造(附高清图) 本文详细拆解了超声波清洗机的内部构造,从电路板设计到换能器工作原理,揭示了其核心技术。通过高清图片和详细分析,展示了功率放大电路、高频变压器及压电换能器等关键部件,为电子爱好者和维修技术人员提供了实用的故障诊断和元件二次利用指南。 阅读详情

相关推荐

基于Zynq的便携式γ能谱仪设计与实现

数字多道分析器(DMCA)是核辐射能谱测量的核心设备,其本质是将探测器输出的模拟脉冲按幅度数字化分道,构建能量-计数分布曲线,从而支撑放射性核素识别与定量分析。其技术原理依赖高速采样、数字滤波成形、基线恢复与峰值提取等关键信号处理环节,具备高分辨率、优线性度与强抗干扰能力。在环境监测、核安全巡检与野外地质勘探等场景中,对低功耗、小型化、实时性提出严苛要求。本方案采用Xilinx Zynq-7020异构SoC架构,融合FPGA实时数字信号处理与ARM嵌入式控制,实现软硬协同优化,兼顾算法精度与工程落地性,为国

weixin_33173126的博客 190

loadClass()和forName()的区别

类加载的方式 主要有两种: 隐式加载:使用 new + 构造方法时,隐式的调用类加载器,加载对应的类到 JVM 中,是最常见的类加载方式。 显式加载:使用 loadClass()、forName() 等方法显式的加载需要的类,对于显式加载这种类加载方式来讲,当我们获取到了 Class 对象后,需要调用 Class 对象的 newInstance() 方法来生成对象的实例。 两种类加载方式的区别...

To be a great coder 7997

【SEW-伺服电机-Profinet-io通讯控制——伺服配置】

SEW-伺服电机-Profinet-io通讯控制——伺服配置一、本次使用配置说明编写经历受国际形式与疫情影响相关资料查询阅读测试验证不易二、伺服配置流程1.基本准备条件2.硬件-电路连接3.设备在线连接4.软件-参数配置5.手动运行测试6.PN-IO自动控制配置8.PLC1200硬件组态9.PLC软件编程10.调试故障分析三、总结四、参考 一、本次使用配置说明编写经历 受国际形式与疫情影响 由于近期受国际形式影响以及全球疫情影响,Siemens等常用产品供货期与供货量严重受限,项目上不得不采取选用其它替代产

qq_39024187的博客 1万+

Class的forName方法与ClassLoader的loadClass方法的区别

菜到怀疑人生的博客 896

openEuler Embedded新增软件包及在线安装软件方法

openEuler Embedded是基于Yocto构建的嵌入式操作系统,默认不包含dnf/apt软件包管理器,主要使用rpm格式。新增软件需通过manifest文件检查是否集成,未集成则需适配新配方。对于在线安装需求,可采用轻量级容器方案:通过isulad运行openEuler服务器版容器镜像,在容器内使用dnf命令安装软件(如Nginx),实现服务器生态兼容。部署前提包括设备支持、存储空间(≥10GB)和网络连接,需先安装并启动isulad服务。这种方法结合了嵌入式系统的轻量化特性和服务器系统的软件管理

专注嵌入式开发与 Linux 系统应用实战,分享芯片驱动、设备调试、系统工具开发干货。 1225

手机VPN的连接方法分享

6、返回到VPN页面,可以看到刚刚新添加的VPN,点击开启VPN后面的开关。5、在打开的添加VPN的页面中输入VPN的信息后点击右上角的“保存”按钮。7、待VPN开关开启后,可以看到添加的VPN正在连接的提示信息了。2、在打开的小米手机设置页面中点击“更多连接方式”的菜单项。3、然后在打开的更多连接方式页面中点击VPN的菜单项。4、在打开的VPN设置页面中点击“添加VPN”的按钮。1、首先在手机屏幕左右的滑动,找到手机的设置图标。

惠惠软件 3万+

01-JVM-类加载篇

/*** 编写findClass方法的逻辑*/@Override// 获取类的class文件字节数组} else {//直接生成class对象}}/*** 编写获取class文件并转换为字节码流的逻辑 * @param className * @return*/// 读取类文件的字节try {// 读取类文件的字节码= -1) {}}}/*** 类文件的完全路径*/}try {//创建自定义的类的加载器1。

qq_42861526的博客 383

ABAP CDS Time Functions 全面解析:用 TIMS_IS_VALID 护航时刻字段的正确性

摘要:本文深入解析ABAP CDS时间函数TIMS_IS_VALID的应用,重点介绍其在企业系统时间字段校验中的关键作用。文章指出无效时间数据会导致业务流程混乱,而CDS层校验能有效减少重复逻辑。详细说明TIMS类型格式要求、空值处理规则,并提供两种工程实现方案:在CDS视图中直接派生校验列,或通过ABAP SQL在内存表中批量验证。最后给出可直接运行的代码示例,包括一个ABAP报表演示内存表验证,以及一个CDS View Entity实现系统时间自动映射与校验,帮助开发者在业务场景中快速落地时间校验功能。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 193

java:classLoader.loadClass() 和 Class.forName()

Java虚拟机把描述类的数据,从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的java类型,这个过程被称为虚拟机的类加载机制。- 《深入理解java虚拟机》

a232884c的博客 2492

lammps案例:聚乙烯和石墨烯界面pull out模拟

大家好,我是小马老师。 本文介绍lammps模拟计算界面结合力的一个案例。 本案例参考论文《The interfacial mechanical properties of functionalized graphene- polymer nanocomposites》,对论文作者表示感谢。 模型共分2层,上层为聚乙烯模型,下次为单层石墨烯,模拟过程共分3部分:建模、弛豫、拉伸。 (1)建模 使用MS建立聚乙烯和石墨烯模型,具体方法可参考之前的推文,使用build layer方法建立聚乙烯和石墨烯组合模型,

lammps_jiayou的博客 3491

C# 工业上位机集成 YOLOv8 全实战:模型转换→相机取流→PLC 联动落地部署

// 检测结果实体类,工业场景扩展置信度、坐标、类别字段set;set;set;set;/// 模型配置常量,与导出参数保持一致// 置信度阈值// 非极大值抑制阈值public static readonly string[] ClassNames = { "物料", "缺陷", "挡板" };// 自定义类别本文完整实现了YOLO模型转换→C#上位机集成→工业相机对接→PLC联动→现场部署标准化模型转换流程,杜绝C#端兼容性问题;解耦相机取流、推理、UI线程,满足工业高稳定性要求;

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 318

Class.forName()的理解

转自:http://blog.youkuaiyun.com/yanwushu/article/details/7574713 使用jdbc方式连接数据库时会使用一句代码Class.forName(StringclassName).这句话是什么意思呢?首先说一点Class.forName(StringclassName)这个方法的作用是装载className这个字符串指定的类。 ...

weixin_30666401的博客 236

OpenClaw实战系列04:Jira/Confluence深度集成实战指南

汇总 Confluence 空间 TEAM 下所有关于“性能优化”的文档,输出要点自然语言创建和查询 Jira 任务,无需离开聊天工具基于 Confluence 的私有知识库问答,让 AI 真正理解团队文档Jira 与 Confluence 联动,打通项目管理与知识管理定时同步与通知,自动化团队信息流转至此,你的 OpenClaw AI 助理已经不仅是一个聊天机器人,而是真正融入团队工作流的“智能项目管理助手”。下一期我们将带来OpenClaw 自定义技能开发实战。

PeterPan的博客 572

JAVA学习之ClassLoader

JAVA学习之ClassLoader 前言 最近被 一句话所触动——种一棵树最好的时间是十年前,其次是现在。所以决定要开始记录自己的学习之路。 什么是类加载? 我们都知道,每个.java文件可以经过javac指令编译成.class文件,里面包含着java虚拟机的机器指令。当我们需要使用一个java类时,虚拟机会加载它的.class文件,创建对应的java对象。将.class调入虚拟机的过程,称之为加载。 loading :加载。通过类的完全限定名找到.class字节码文件,同时创建一个对象。...

marcushbs的博客 1430

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

ClassLoader.loadClass和Class.forName的区别

为什么要把ClassLoader.loadClass(String name)和Class.forName(String name)进行比较呢,因为他们都能在运行时对任意一个类,都能够知道该类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性。 在比较它俩之前需先了解一下java类装载的过程 java类装载过程分为3步:      1:加载     Jvm把clas...

weixin_30666401的博客 356

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

自定义类加载器的一些问题

一、同一个类文件被不同类加载器加载,它们的Class对象不相等。 执行主类代码如下: import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; /** * @Author 周利东 * @Date: 2019/1/18 9:47 */ public class ...

祁东握力的博客 468

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

显式意图和隐式意图分别在什么场合使用?

1、显式意图一般在应用的内部使用,因为在应用内部已经知道了组件的名称直接激活就行了。 2、隐式意图是在应用与应用之间使用,当一个应用要激活另一个应用中的Activity时看不到源代码,只能使用隐式意图,根据Activity配置的意图过滤器建一个意图,让意图中的各项参数的值都跟过滤器匹配,这样就可以激活其他应用中的Activity。所以,隐式意图是在应用与应用之间使用的。 3、如果自己开发的

beyond0851的专栏 2902

JVM 从入门到精通(六)JVM运行时数据区——虚拟机栈

由于跨平台性的设计,Java的指令都是根据栈来设计的。不同平台CPU架构不同,所以不能设计为基于寄存器的。根据栈设计的优点是跨平台,指令集小,编译器容易实现,缺点是性能下降,实现同样的功能需要更多的指令。

云祁QI 1083

node.js自定义模块中的module.exports

1.node.js的模块作用域 和函数作用域类似,在自定义模块中定义的变量方法等成员,只能在当前模块内访问,这种模块识别的访问机制,叫模块作用域。模块作用域的好处是防止全局变量的污染。 但仍有方法向外暴露自定义模块中的成员,以供外界使用。 2.向外共享模块作用域中的成员 相关的几个概念: module module:每个.js自定义模块中都有一个module对象,存储了和当前模块有关的信息 module.exports 在自定义模块中,可以使用module.exports对象,把模块中的成员共享出去

qq_52868203的博客 542

尚硅谷JVM从入门到精通宋红康版|第二章、类加载子系统

类加载子系统 概述 完整图如下 如果自己想手写一个Java虚拟机的话,主要考虑哪些结构呢? 类加载器 执行引擎 类加载器子系统作用 类加载器子系统负责从文件系统或者网络中加载Class文件,class文件在文件开头有特定的文件标识。 ClassLoader只负责class文件的加载,至于它是否可以运行,则由Execution Engine决定。 加载的类信息存放于一块称为方法区的内存空间。除了类的信息外,方法区中还会存放运行时常量池信息,可能还包括字符串字面量和数字常量(这部分常量信息是Class文

weixin_45759791的博客 1700

反射中,Class.forName和ClassLoader.loadCalss方法的区别

反射中Class.forName()和ClassLoader.loadClass()的区别 一 Java类装载过程 装载:通过累的全限定名获取二进制字节流,将二进制字节流转换成方法区中的运行时数据结构,在内存中生成Java.lang.class对象; 链接:执行下面的校验、准备和解析步骤,其中解析步骤是可以选择的;   校验:检查导入类或接口的二进制数据的正确性;(文件格式

小圣_fighting的博客 3392

Spring 中的类加载机制 - ClassLoader

Spring 中的类加载机制 - ClassLoader ClassLoader JVM 类加载机制系列文章: Java - ClassLoader Spring 中的类加载机制 - ClassLoader 一、OverridingClassLoader 中的使用 OverridingClassLoader 是 Spring 自定义的类加载器,默认会先自己加载(excludedPackag...

每天进步一点点的专栏 4870

node基本指南2

node基本指南1在这:https://blog.youkuaiyun.com/qq_41624138/article/details/108653049 本文目录: ◆ 能够使用 http 模块写一个基本的 web 服务器 ◆ 能够说出模块化的好处 ◆ 能够知道 CommonJS 规定了哪些内容 ◆ 能够说出 Node.js 中模块的三大分类各自是什么 ◆ 能够使用 npm 管理包 什么是 http 模块 http 模块是 Node.js 官方提供的、用来创建 web 服务器的模块。通过 http 模块提供

qq_41624138的博客 354

类的显式与隐式加载

类的显式与隐式加载 ​ 类的加载方式是指虚拟机将class文件加载到内存的方式。 ​ 显式加载是指在java代码中通过调用ClassLoader加载class对象,比如Class.forName(String name);this.getClass().getClassLoader().loadClass()加载类。 ​ 隐式加载指不需要在java代码中明确调用加载的代码,而是通过虚拟机自动加载到内存中。比如在加载某个class时,该class引用了另外一个类的对象,那么这个对象的字节码文件就会被虚拟

Leon_Jinhai_Sun的博客 990

深入理解Java类加载器(ClassLoader)

【版权申明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) 本篇博文主要是探讨类加载器,同时在本篇中列举的源码都基于Java8版本,不同的版本可能有些许差异。主要内容如下 类加载的机制的层次结构 每个编写的".java"拓展名类文件都存储着需要执行的程序逻辑,这些".java"文件经过Java编译器编译成拓展名为".class"的文件,".class"文件中保存着Java代码经转换...

weixin_34228617的博客 8877

我所知道JVM虚拟机之类的加载器五(沙箱与自定义加载器)

前言 本篇文章介绍一些什么是沙箱机制和自定义加载器有什么好处和怎么操作 沙箱安全机制 沙箱安全机制主要有以下特征 保证程序安全 保护Java原生的JDK代码 Java安全模型的核心就是Java沙箱(sandbox) 什么是沙箱?沙箱是一个限制程序运行的环境 沙箱机制就是将]ava代码限定在虚拟机(JVN)特定的运行范围中,并且严格限制代码对本地系统资源访问 通过这样的措施来保证对代码的有限隔离,防止对本地系统造成破坏。 沙箱主要限制系统资源访问,那系统资源包括什么?CPU、内存、文件系统、网络 不同级别

我吃西红柿 372
上一篇: 尚硅谷2020最新版宋红康JVM教程-中篇-第4章:再谈类的加载器-02和03-类的加载器分类
下一篇: 尚硅谷2020最新版宋红康JVM教程-中篇-第4章:再谈类的加载器-05-双亲委派模型
zgcadmin
博客等级 码龄9年 174粉丝 109原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值