Java学习笔记

1、微服务

链接:Spring Cloud微服务简介
微服务各个服务可以独立部署,各个服务之间是松耦合的,每个微服务只关注完成一个或少数任务。每个微服务都有自己独立处理通讯的机制,可以部署在一个或多个服务器上。

Spring Cloud的注册中心可以由Eureka、Consul、Zookeeper、ETCD等来实现,这里推荐使用Spring Cloud Eureka来实现注册中心,它基于Netfilix的Eureka做了二次封装,完成分布式服务中服务治理的功能,微服务系统中的服务注册与发现都通过这个注册中心来进行管理。通过SpringCloud Ribbon实现负载均衡。Spring Cloud Gateway作为路由网关,实现路由转发。

服务容错保护Spring Cloud Hystrix:

  • 一次请求依赖多个微服务。
  • 单个服务崩溃导致雪崩。某一服务出现异常,拖垮整个服务链路,消耗整个线程队列,造成服务不可用,资源耗尽。

解决雪崩效应:
链接:熔断机制hystrix

  • 资源隔离:包括线程池隔离和信号量隔离,限制调用分布式服务的资源使用,某一个调用的服务出现问题不会影响其他服务调用。
  • 降级机制:超时降级、资源不足时(线程或信号量)降级,降级后可以配合降级接口返回托底数据。
  • 熔断:由于某些原因使得服务出现了过载现象,为防止造成整个系统故障,从而采用的一种保护措施,所以很多地方把熔断亦称为过载保护。如果某个目标服务调用慢或者有大量超时,此时,熔断该服务的调用,对于后续调用请求,不在继续调用目标服务,直接返回,快速释放资源。如果目标服务情况好转则恢复调用。
  • 缓存:提供了请求缓存、请求合并实现。

资源隔离:

  • 线程池隔离模式:使用一个线程池来存储当前请求,线程池对请求作处理,设置任务返回处理超时时间,堆积的请求先入线程池队列。这种方式要为每个依赖服务申请线程池,有一定的资源消耗,好处是可以应对突发流量(流量洪峰来临时,处理不完可将数据存储到线程池队里慢慢处理)
  • 信号量隔离模式:使用一个原子计数器(或信号量)记录当前有多少个线程在运行,请求来先判断计数器的数值,若超过设置的最大线程个数则丢弃该类型的新请求,若不超过则执行计数操作请求来计数器+1,请求返回计数器-1。这种方式是严格的控制线程且立即返回模式,无法应对突发流量(流量洪峰来临时,处理的线程超过数量,其他的请求会直接返回,不继续去请求依赖的服务)

微服务优点:1、解耦;2、分布式;

微服务缺点:通常一个操作可能涉及多个微服务,如果为了完成一个操作而多次从服务端调用不同的微服务,http请求的耗时可能会成为瓶颈。

springcloud是基于RESTful(http+json)架构的。

Dubbo是基于RPC(remote procedure call)实现远程调用。

2、负载均衡

  • SpringCloud Ribbon:所有的服务通过Eureka 进行注册到注册中心,客户端的调用就可以利用 Ribbon 技术来实现。Ribbon 是一个服务调用的组件,并且是一个客户端实现负载均衡处理的组件。服务器端实现负载均衡可以使用 Nginx、 HAProxy、LVS 等。
  • 负载均衡集群:作用将大量的并发请求分担到多个处理节点,由于单个处理节点的故障不影响整个服务,负载均衡集群同时也实现了高可用性。一般提到的负载均衡(Load Balance),是指实现负载均衡集群。负载均衡实现了横向扩展(Scale Out),避免纵向的升级(Scale Up)换代。服务器端的负载均衡可以通过Nginx、HAProxy、LVS 等。
  • MySQL高可用架构之MHA。

3、cookie和session的关系

服务器收到第一次请求,开辟一块Session空间,生成一个SessionId,并通过响应头的Set-Cookie:“JSESSIONID=XXXXXXX”命令,向客户端发送设置cookie的响应;客户端收到响应,在本机浏览器设置JSESSIONID的cookie信息,该cookie的生命周期为会话结束。以后每次浏览器每次向服务端请求时都会带上这个cookie信息,服务端通过读取请求头中的cookie信息,获取sessionId。服务端通过sessionId判断是不是同一个会话。

4、get和post的区别

GET请求在URL中传送的参数是有长度限制的,而POST没有。
GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
GET参数通过URL传递,POST放在Request body中。
GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
GET请求只能进行url编码,而POST支持多种编码方式。
GET请求会被浏览器主动cache,而POST不会,除非手动设置。
GET产生的URL地址可以被Bookmark,而POST不可以。
GET在浏览器回退时是无害的,而POST会再次提交请求。

get和post本质上没有区别,底层都是通过TCP传输数据。
get请求只产生一个数据包;post产生两个数据包(跟浏览器有关,我用chrome60试了试,用抓包工具抓包测试,发现只发了一个数据包);
get请求,浏览器会把header和data一起发过去,服务器响应200。
post请求浏览器先发送header,服务器响应100;后发送data,服务器响应200。
axios给后端发送post请求,如果设置带cookie,则是请求两次,第一次发送OPTIONS请求,第二次发送post请求。

5、Websocket和Socket、http的区别和联系

  • Socket并不是一个协议,而是为了方便用户使用TCP和UDP而抽象出来的一层API接口。
  • Websocket和http一样,是一个应用层协议。
  • Websocket和http都是基于TCP/IP的可靠型传输协议。
  • Websocket是双向通信协议,可以双向发送和接收信息;http是单向的。
  • Websocket是需要浏览器和服务器握手建立连接;http是浏览器向服务器发起的连接,服务器预先不知道该连接。
  • Websocket在建立握手时,数据是通过http传输的,连接建立后的数据传输是不需要http的,是通过TCP进行数据传输。
    在这里插入图片描述

6、三次握手和四次挥手

链接:TCP与UDP的区别以及TCP的三次握手四次挥手

字段含义
URG紧急指针是否有效,为1,表示某一位需要被优先处理。
ACK确认号是否有效,一般置为1。
PSH提示接收端应用程序立即从TCP缓冲区把数据读走。
RST对方要求重新建立连接,复位。
SYN请求建立连接,设置为1。
FIN希望断开连接。FIN=1表示:此报文段的发送方的数据已经发送完毕,并要求释放运输连接。

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

7、MySQL数据库引擎MyISAM和InnoDB的区别

  • 事务支持

MyISAM:不支持事务,强调的是性能,是非事务安全的,当select查询比较多,用MyISAM比较合适。

InnoDB:支持事务,支持外键,是事务安全的,默认开启自动提交,宜合并事务,一同提交,减小数据库多次提交导致的开销,大大提高性能。具有事务(commit)、回滚(rollback)和崩溃修复能力(crash recovery capabilities)的事务安全(transaction-safe (ACID compliant))型表。当update和insert比较多时,用InnoDB合适。

  • 存储结构

MyISAM:每个数据表在磁盘上存储成三个文件,.FRM文件存储表定义,.MYD数据文件,.MYI索引文件。

InnoDB:所有的表都保存在同一个数据文件中,InnoDB表的大小只受限于操作系统的大小,一般为2GB。

  • 存储空间

MyISAM:可被压缩,存储空间较小。
InnoDB:需要更多的内存和存储,它会在主内存中建立其专用的缓冲池用于高速缓冲数据和索引。

  • 全文索引

MyISAM:支持(FULLTEXT类型的)全文索引
InnoDB:不支持(FULLTEXT类型的)全文索引,但是innodb可以使用sphinx插件支持全文索引,并且效果更好。

  • 表的总行数

MyISAM:保存有表的总行数,如果select count() from table;会直接取出出该值。
InnoDB:没有保存表的总行数(只能遍历),如果使用select count(
) from table;就会遍历整个表,消耗相当大,但是在加了wehre条件后,myisam和innodb处理的方式都一样。

8、MySQL删除重复数据,只保留一条

在这里插入图片描述
在这里插入图片描述
delete from dept where dname in (select t.dname from (select dname from dept group by dname having count(1) > 1)t)

9、collection和map的结构

Collection:List、Set、Queue

  • List:
    • ArrayList:底层是数组,非同步的。
    • Vector:跟ArrayList类似,也是数组,是同步的,线程安全的,但是效率低,用的少。
    • LinkedList:底层是链表。
  • Set:
    • HashSet:为快速查找而设计的set,存入HashSet的对象必须定义HashCode和equals。
    • TreeSet:基于红黑树的实现,存入的数据会排序。
    • LinkedHashSet:类似于HashSet,不过用链表维护元素的顺序(插入顺序)。
  • Queue:扩展了Queue接口,增加了很多其它的方法。
    • LinkedList:基于链表的实现,是一个普通队列,具有入队出队等操作。
    • PriorityQueue:是一个比较标准的队列实现类,之所以说其标准,是因为其保存队列元素不是按其入队顺序,而是按照队列大小对其重新排序,因此出队的元素通常是最小元素。
    • Deque:基于Queue的子类。
      • ArrayDeque:基于数组的实现,具有入队出队等操作。
      • LinkedList:基于链表的实现,具有入队出队等操作。

Map:

  • HashMap:基于哈希表的实现,它取代了HashTable。
  • TreeMap:基于红黑树的实现,存入的数据是要经过排序的。
  • LinkedHashMap:类似于HashMap,区别是它采用链表维护元素顺序(插入顺序)。

10、Java内存模型

  • 栈:函数中的局部变量和函数调用过程中的临时变量,还有对象的引用都存在该区域。
  • 堆:对象实例存在该区域。
  • 方法区:包含常量池,存放了要加载的类的信息(名称、修饰符等)、静态常量、final类型的常量、Field信息、方法信息。方法区是被Java线程锁共享的,不像Java堆中其他部分一样会频繁被GC回收。
  • 程序计数器:记录线程执行的字节码的行号。

11、JVM垃圾回收原理

链接:JVM回收机制

栈:又称方法栈,线程私有的,线程执行方法是都会创建一个栈阵,用来存储局部变量表,操作栈,动态链接,方法出口等信息.调用方法时执行入栈,方法返回式执行出栈。类的成员变量,没有实例化类的时候是存在栈中,实例化后跟随对象存在堆中,栈中存放的是对象的引用。
本地方法栈:与栈类似,也是用来保存执行方法的信息.执行Java方法是使用栈,执行Native方法时使用本地方法栈。
程序计数器:保存着当前线程执行的字节码位置,每个线程工作时都有独立的计数器,只为执行Java方法服务,执行Native方法时,程序计数器为空。
堆:JVM内存管理最大的一块,对被线程共享,目的是存放对象的实例,几乎所欲的对象实例都会放在这里,当堆没有可用空间时,会抛出OOM异常.根据对象的存活周期不同,JVM把对象进行分代管理,由垃圾回收器进行垃圾的回收管理。
方法区:又称非堆区,用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器优化后的代码等数据.1.7的永久代和1.8的元空间都是方法区的一种实现。

12、TCP和UDP的重传机制

TCP:

UDP:应用层实现采用超时重传,实现udp的可靠传输。在规定的时间内,若没有收到sendto的信息,我们就认为数据包丢失,打破recvfrom的阻塞式等待,设置一个闹钟定时器alarm,当收到SIGALRM信号时,程序中断,转去处理中断响应函数,通知客户端进行重传。

13、https和http的区别

HTTPS(Secure Hypertext Transfer Protocol)安全超文本传输协议。
它是一个安全通信通道,基于HTTP开发,用于在客户计算机和服务器之间交换信息。它使用安全套接字层(SSL)进行信息交换,简单来说它是HTTP的安全版。
它是由Netscape开发并内置于其浏览器中,用于对数据进行压缩和解压操作,并返回网络上传送回的结果。HTTPS实际上应用了Netscape的安全套接字层(SSL)
作为HTTP应用层的子层。(HTTPS使用端口443,而不是象HTTP那样使用端口80来和TCP/IP进行通信。)

HTTPS和HTTP的区别:
https协议需要到ca申请证书,一般免费证书很少,需要交费。
http是超文本传输协议,信息是明文传输,https 则是具有安全性的ssl加密传输协议。
http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
http的连接很简单,是无状态的。
HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。

14、GC是什么时候触发的

由于对象进行了分代处理,因此垃圾回收区域、时间也不一样。GC有两种类型:Scavenge GC和Full GC。

Scavenge GC:
一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,对Eden(新new的对象会放在该区域,经过GC回收幸存下来的会存入Survivor Space幸存者区)区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区进行,不会影响到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden去能尽快空闲出来。

Full GC:
对整个堆进行整理,包括Young、Tenured和Perm。Full GC因为需要对整个堆进行回收,所以比Scavenge GC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分工作就是对于Full GC的调节。有如下原因可能导致Full GC:
a) 年老代(Tenured)被写满;
b) 持久代(Perm)被写满;
c) System.gc()被显示调用;
d) 上一次GC之后Heap的各域分配策略动态变化;

15、字符流和字节流的区别

Java 流在处理上分为字符流和字节流。字符流处理的单元为 2 个字节的 Unicode 字符,分别操作字符、字符数组或字符串,而字节流处理单元为 1 个字节,操作字节和字节数组。

Java 内用 Unicode 编码存储字符,字符流处理类负责将外部的其他编码的字符流和 java 内 Unicode 字符流之间的转换。而类 InputStreamReader 和 OutputStreamWriter 处理字符流和字节流的转换。字符流(一次可以处理一个缓冲区)一次操作比字节流(一次一个字节)效率高。

16、Spring AOP和Spring IOC

  • IOC:控制反转,具有依赖注入功能的容器,可以创建对象的容器;传统的程序设计中通常是由调用者创建被调用者的实例;在Spring中,创建被调用者的工作不再由调用者来完成,由IOC控制对象的创建,因此称为控制反转。IOC容器管理bean的生命周期,一旦创建这个对象,就不再帮你管理这个对象了,交给GC管理。
  • DI:依赖注入,容器创建对象后,处理对象的依赖关系。组件之间的依赖关系由容器运行期间决定,由容器动态的将某个依赖注入到组件中;依赖注入的目的是为了提升组件重用的频率,为系统搭建一个灵活可扩展的平台。依赖注入是利用反射实现注入的,反射允许程序运行时动态的生成对象,执行对象的方法,改变对象的属性。**控制反转和依赖注入是同一个概念不同角度的描述。**依赖注入的方式:set注入方式,静态工厂注入方式,构造方法注入方式,基于注解的方式。
  • AOP:面向切面编程,把那些分散在各处且与对象核心功能无关的代码(横切代码)的存在,使得模块复用难度增加。AOP则将封装好的对象剖开,找出其中对多个对象产生影响的公共行为,并将其封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),切面将那些与业务无关,却被业务模块共同调用的逻辑提取并封装起来,减少了系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。比如日志记录,拦截器,事务管理。
    • 连接点(join point):对应的是具体被拦截的方法,在SpringbootAOP例子中printUser就是一个连接点,AOP通过动态代理的技术把它织入对应流程中。
    • 切点(point cut):我们的切面不单单是应用于单个方法,通过正则表达式去适配连接点,让切面应用于多个连接点。
    • 通知(advice):前置通知、后置通知、环绕通知、事后返回通知和异常通知。
    • 目标对象(target):即被代理对象,连接点所在的对象。
    • 引入(introduction):引入新的类和方法,增强现有Bean的功能。就是引入切面,实现Bean的增强。
    • 织入(weaving):通过动态代理技术,将约定织入流程的过程。
    • 切面(aspect):是一个可以定义切点、各类通知和引入的内容,Spring AOP通过切面增强Bean的功能或者将对应的方法织入流程。

17、Springboot和SpringMVC的区别

  • Spring 是一个“引擎”。
  • Spring MVC是Spring的一个模块,是一个web框架。通过Dispatcher Servlet, ModelAndView 和 View Resolver让开发web应用变得很容易。解决的问题领域是网站应用程序或者服务开发——URL路由、Session、模板引擎、静态Web资源等等。
  • Springboot是基于Spring的快速开发整合包,Spring Boot有一套默认配置,我们可以把它看做比较通用的约定,而Spring Boot遵循的也是约定优于配置原则,在Spring Boot中,你会发现你引入的所有包都是*starter(懒人整合包)*形式。

18、Sprngboot事务

  • 原子性(Atomicity):事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。
  • 一致性(Consistency):一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏。
  • 隔离性(Isolation):可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。
  • 持久性(Durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。
  • Spring事务:事务是一系列的动作,它们综合在一起才是一个完整的工作单元,这些动作必须全部完成,如果有一个失败的话,那么事务就会回滚到最开始的状态,仿佛什么都没发生过一样。 在企业级应用程序开发中,事务管理必不可少的技术,用来确保数据的完整性和一致性。

19、Java并发多线程

创建线程:
  • 1、继承Thread类创建线程;
  • 2、实现Runnable接口创建线程;
  • 3、实现Callable接口创建线程;
  • 4、通过线程池创建多线程;
继承Thread和实现Runnable接口的区别
  • 实现Runnable接口避免多继承的局限;
  • 实现Runnable接口更好的体现共享的概念;
CPU资源占用情况:
  • cpu划分时间片给多线程,同一个时间片CPU只执行一个线程,CPU高速切换线程的执行,好像是线程一块执行的,起始是高速切换。
线程放弃CPU使用权的情况:
  • 线程暂时放弃CPU操作,例如调用yield()方法。
  • 当前线程进入阻塞状态,例如I/O阻塞。
  • 当前线程执行结束,即运行完run()方法。

20、Springboot整合MyBatis

21、Spring生成对象默认是单例的,通过scope属性可以更改为多例

22、Java中Sleep和Wait的区别

  • 对于sleep()方法,我们首先要知道该方法是属于Thread类中的。而wait()方法,则是属于Object类中的。

  • sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。在调用sleep()方法的过程中,线程不会释放对象锁。

  • 虽然两者都是用来暂停当前运行的线程,但是 sleep() 实际上只是短暂停顿,因为它不会释放锁,而 wait() 意味着条件等待,这就是为什么该方法要释放锁,因为只有这样,其他等待的线程才能在满足条件时获取到该锁。

23、Redis数据实时同步数据库的问题

24、如何处理异常和常见的异常

链接:Java 中的异常和处理详解

非检查异常和检查异常
  • 非检查异常(unckecked exception):Error 和 RuntimeException 以及他们的子类。javac在编译时,不会提示和发现这样的异常,不要求在程序处理这些异常。所以如果愿意,我们可以编写代码处理(使用try…catch…finally)这样的异常,也可以不处理。对于这些异常,我们应该修正代码,而不是去通过异常处理器处理 。这样的异常发生的原因多半是代码写的有问题。如除0错误ArithmeticException,错误的强制类型转换错误ClassCastException,数组索引越界ArrayIndexOutOfBoundsException,使用了空对象NullPointerException等等。
  • 检查异常(checked exception):除了Error 和 RuntimeException的其它异常。javac强制要求程序员为这样的异常做预备处理工作(使用try…catch…finally或者throws)。在方法中要么用try-catch语句捕获它并处理,要么用throws子句声明抛出它,否则编译不会通过。这样的异常一般是由程序的运行环境导致的。因为程序可能被运行在各种未知的环境下,而程序员无法干预用户如何使用他编写的程序,于是程序员就应该为这样的异常时刻准备着。如SQLException , IOException,ClassNotFoundException 等。
运行时异常和非运行时异常
  • 都是RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是非检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。
  • 是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不要自定义检查异常。
自定义异常

如果要自定义异常类,则扩展Exception类即可,因此这样的自定义异常都属于检查异常(checked exception)。如果要自定义非检查异常,则扩展自RuntimeException。按照国际惯例,自定义的异常应该总是包含如下的构造函数:

  • 一个无参构造函数
  • 一个带有String参数的构造函数,并传递给父类的构造函数。
  • 一个带有String参数和Throwable参数,并都传递给父类构造函数
  • 一个带有Throwable 参数的构造函数,并传递给父类的构造函数。
try … catch … finally…
try{
      //try块中放可能发生异常的代码。
      //如果执行完try且不发生异常,则接着去执行finally块和finally后面的代码(如果有的话)。
      //如果发生异常,则尝试去匹配catch块。
  }catch(SQLException SQLexception){
      //每一个catch块用于捕获并处理一个特定的异常,或者这异常类型的子类。Java7中可以将多个异常声明在一个catch中。
      //catch后面的括号定义了异常类型和异常参数。如果异常与之匹配且是最先匹配到的,则虚拟机将使用这个catch块来处理异常。
      //在catch块中可以使用这个块的异常参数来获取异常的相关信息。异常参数是这个catch块中的局部变量,其它块不能访问。
      //如果当前try块中发生的异常在后续的所有catch中都没捕获到,则先去执行finally,然后到这个函数的外部caller中去匹配异常处理器。
      //如果try中没有发生异常,则所有的catch块将被忽略。
  }catch(Exception exception){
      //...
  }finally{
  //finally块通常是可选的。
  //无论异常是否发生,异常是否匹配被处理,finally都会执行。
  //一个try至少要有一个catch块,否则, 至少要有1个finally块。但是finally不是用来处理异常的,finally不会捕获异常。
  //finally主要做一些清理工作,如流的关闭,数据库连接的关闭等。 
  //良好的编程习惯是:在try块中打开资源,在finally块中清理释放这些资源。
  //首先一个不容易理解的事实:在 try块中即便有return,break,continue等改变执行流的语句,finally也会执行。
  //不要在fianlly中使用return。finally中的return会抑制(消灭)前面try或者catch块中的异常。会覆盖前面return的值。
  //不要在finally中抛出异常。finally中的异常会覆盖(消灭)前面try或者catch中的异常。
  //减轻finally的任务,不要在finally中做一些其它的事情,finally块仅仅用来释放资源是最合适的。
  //尽量将所有的return写在函数的最后面,而不是try...catch...finally...中。
  }

25、整体性能方面的考虑

26、Linux常用命令

  • awk
  • top
  • netstat
  • grep
  • less
  • tail
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值