java常问面试题总结

重载与重写的区别

(1)方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载 (Overloading)。

(2)方法重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写 (Overriding)。

(3)方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。

方法重载的要求是参数列表不同。具体包括以下三种情形。①参数的数量不同。②参数的类型不同。③参数的顺序不同。————————————————

equals 和==的区别

equals 比较的内容是否相等

== 比较他们的地址是否相等

对象类型不同

1、equals():是超类Object中的方法。

2、==:是操作符。

三、运行速度不同

1、equals():没有==运行速度快。

2、==:运行速度比equals()快,因为==只是比较引用。

集合与数组的区别

1、数组声明了它容纳的元素的类型,而集合不声明。

​ 二、数组是静态的,一个数组实例具有固定的大小,一旦创建了就无法改变容量了。而集合是可以动态扩展容量,可以根据需要动态改变大小,集合提供更多的成员方法,能满足更多的需求。

​ 三、数组不论是效率还是类型检查都是最好的。

1.数组是大小固定的,一旦创建无法扩容;集合大小不固定,

2.数组的存放的类型只能是一种,集合存放的类型可以不是一种(不加泛型时添加的类型是Object);

3.数组是java语言中内置的数据类型,是线性排列的,执行效率或者类型检查(不懂),都是最快的.

ArrayList就是基于数组创建的容器类.

值传递与引用传递区别

(1)按值传递不会改变实际参数的数值; (2)按引用传递可以改变实际参数的内容,但不能改变实际参数的参考地址

errot与exception区别

1、Exception和Error都继承了Throwable类,在java中只有Throwable类型的实例才可以被抛出(Throw)或者捕捉(catch),它是异常处理机制的基本组成类型。Exception是程序正常运行中,可以预料的意外情况,可能并且应该被捕获,进行相应的处理。

  

  2、Error指在正常情况下,不大可能出现的情况,绝大部门的Error都会导致程序处于非正常的,不可恢复状态。既然是非正常情况,所以不便于也不需要捕获。比如常见的OutOfMemoryError之类,都是Error的子类。

 Exception类又分为可检查异常(checked)和不检查异常(unchecked),可检查异常在源码里必须显示的进行捕获处理,这是编译期检查的一部分。不检查异常就是所谓的运行时异常,类似NullPointerException,ArrayIndexOutOfBoundsException之类,通常是可以编码避免的逻辑错误。

files常用的方法有哪些

Files. exists():检测文件路径是否存在。 Files. createFile():创建文件。 Files. createDirectory():创建文件夹。 Files. delete():删除一个文件或目录。 Files. copy():复制文件。 Files. move():移动文件。 Files. size():查看文件个数。 Files. read():读取文件。 Files. write():写入文件。

创建线程的四种方法

继承THread类

实现runable接口

实现callable接口

创建线程池

比较:

继承Thread类

优点: 编写简单,如果需要访问当前线程,无需使用Thread.currentThread()方法,

直接使用this即可获得当前线程

缺点: 自定义的线程类已继承了Thread类,

所以后续无法再继承其他的类
实现Runnable接口

优点: 自定义的线程类只是实现了Runnable接口或Callable接口,

后续还可以继承其他类,在这种方式下,多个线程可以共享同一个target对象,
所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码、
还有数据分开(解耦),形成清晰的模型,较好地体现了面向对象的思想

缺点: 编程稍微复杂,

如想访问当前线程,则需使用Thread.currentThread()方法。

springboot加载顺序

bootstrap.yml

> bootstrap.properties
    > application.yml
        > application.properties
            >${spring.application.name}-${spring.cloud.config.profile}.yml
                >${spring.application.name}-${spring.cloud.config.profile}.properties

————————————————

Spring Boot-2-核心注解

@SpringBootApplication

@SpringBootConfiguration

@EnableAutoConfiguration

@ComponentScan

Spring Boot会扫描主类所在的包路径下的注解。

springCloud五大核心组件

服务发现——Netflix Eureka客服端

负载均衡——Netflix Ribbon

断路器——Netflix Hystrix

服务网关——Netflix Zuul(本文以gateway取代)

分布式配置——Spring Cloud Config

Mybatis中的${}和#{}区别

1、符号类型

(1)#{}:参数占位符,即预编译 (2)${} :字符串替换符,即SQL拼接 2、防注入问题

(1)#{}:很大程度上能防止sql 注入 (2)${}:不能防止sql 注入

3、参数替换位置

DBMS:数据库管理系统(Database Management System)是一种操纵和管理数据库的大型软件,是用于建立、使用和维护数据库,简称DBMS。它对数据库进行统一的管理和控制,以保证数据库的安全性和完整性。用户通过DBMS访问数据库中的数据,数据库管理员也通过DBMS进行数据库的维护工作。它提供多种功能,可使多个应用程序和用户用不同的方法在同时或不同时刻去建立,修改和询问数据库。

(1)#{}:变量替换是在DBMS 中(2)${}:变量替换是在 DBMS 外4、参数解析

(1)#{}:将传入的数据都当成一个字符串,会对传入的变量自动加一个单引号。如:user_id = #{userId},如果传入的值是111,那么解析成sql时的值为user_id = ‘111’,如果传入的值是id,则解析成的sql为user_id = ‘id’。

(2):将传入的参数直接显示生成在中,且不加任何引号。如:​{userId},如果传入的值是111,那么解析成sql时的值为user_id = 111 , 如果传入的值是id,则解析成的sql为user_id = id。

Git的常用命令与工作流程

git init # 初始化仓库git clone # 克隆远程仓库上的项目到本地

git add # 添加文件=仓库git status # 查看仓库当前的状态,显示有变更的文件git diff # 比较文件的不同,即暂存区和工作区的差异git commit # 提交暂存区到本地仓库git reset # 回退版本git rm # 删除工作区文件git mv # 移动或重命名工作区文件

git log # 查看历史提交记录git blame <file> # 以列表形式查看指定文件的历史修改记录

git remote # 远程仓库操作git fetch # 从远程获取代码库git pull # 下载远程代码并合并git push # 上传远程代码并合并

Linux常用命令

pwd 显示当前目录的绝对路径 ls 选项 查看当前目录下的内容内容信息

ls -a:显示当前目录下的所有内容

ls -l:以列表形式显示所有内容​ cd [参数] 切换到指定目录

cd~ OR cd :回到家目录

cd.. :回到上一级目录​ mikdir [选项] 目录 创建目录 mkdir -p 目录 :创建多级目录 rmdir [选项] 目录 删除空目录

rmdir /home/config :删除/home/config这个空目录

rmdir -rf /home/config :删除非空目录/home/config

如果目录非空,则rmdir无法删除。需要使用rmdir -rf

touch 文件名称 创建空文件 touch test.txt:创建空文件test.txt cp [选项] source dest 拷贝文件到指定目录

cp -r /home/aaa /home/bbb :将aaa文件夹递归拷贝到bbb下

cp /home/aaa.txt /home/bbb:将aaa.txt拷贝到bbb下​ rm [选项] 文件OR目录 删除文件或目录

rm -r 目录 :递归删除整个文件夹

rm -f 文件:强制删除文件不提示​ mv 移动文件、目录或者重命名

mv oldNameFile newNameFile :重命名

mv 文件名称 目录 :移动文件​ cat [选项] 文件 查看文件内容,以只读方式打开 cat -n 文件:显示行号

cat只能浏览文件,不能修改文件,为了浏览方便,一般会带上管道命令 | more

cat 文件名 | moremore 文件 全屏按页显示文本文件的内容

该指令打开文件后有如下快捷键:

space:向下翻页

enter:向下翻一行

q:离开该文件,不再显示内容

ctrl + F:向下滚动一屏

ctrl + B:向上滚动一屏

=:输出当前行号

:f:输出文件名和当前行行号​ less 文件 分屏查看文件内容

该指令打开文件后有如下快捷键:

space:向下翻页

pagedown:向下翻一页

pageup:向上翻一页

q:离开该文件,不再显示内容

/字符串:向下搜寻字符串,n:向下查找,N:向上查找

?字符串:向上搜寻字符串,n:向下查找,N:向上查找

功能与more类似,但比more更加强大,less显示文件内容时,并不是一次将整个文件加载之后才显示,而是根据显示需要加载内容,对大型文件具有较高的效率。

echo 选项 输出内容到控制台 echo $PATH:输出当前的环境路径 head [选项] 文件 显示文件开头内容(默认10行)

head 文件:显示文件头10行内容

head -n20 文件:显示文件头20行内容​ tail [选项] 文件 输出文件尾部内容(默认10行)

tail 文件:查看文件后10行

tail -n20 文件:查看文件后20行

tail -f 文件:实时追踪该文档的所有更新​ 时间日期类date 显示当前时间

date :

date +%Y:

date +%m:

date +%d:

date "+%Y-%m-%d %H:%M:%S":显示年月日时分秒​ date -s 时间字符串 设置当前日期 date -s “2020-12-25 10:10:10” cal [选项] 查看日历

cal:查看当前日历

cal 2020:显示2020日历​ 搜索查找类find 目录 搜索指定目录下的文件

选项示例:

find /home -name test.txt:查找/home下的test.txt文件(按文件名查找)

find /home -size +20M:查找home目录下大于20M的文件

                                 (+n 大于,n小于,-n 等于)

find /home -user nobody:查找指定用户名nobody的文件​ grep [选项] 查找内容 文件

grep:过滤查找

| :管道符,将前一个命令的处理结果传递给后面的命令处理。​

选项:

-n :显示匹配行及行号

-i:忽略大小写

示例:

cat hello.txt | grep -n yes :在hello.txt中查找"yes"所在行及行号​ 压缩解压类

gzip 文件

gunzip 文件.gz​

压缩文件为*.gz文件

解压文件​

zip [选项] xxx.zip 文件/目录

unzip [选项] xxx.zip​

压缩文件/文件目录为zip

解压zip文件​

zip -r test.zip /home/:将home下的所有的文件压缩为test.zip

unzip -d /home/ test.zip:将test.zip解压到home下​ tar [选项] XXX.tar.gz 打包文件为.tar.gz格式

选项:

-c 产生.tar打包文件

-v 显示详细信息

-f 指定打包后的文件名

-z 打包同时压缩

-x 解压、包.tar文件

示例: tar -zxvf 文件名————————————————

String和StringBuffer和StringBuilder的区别

1

String:适用于少量的字符串操作。 StringBuilder:适用于单线程下在字符串缓冲区进行大量操作。 StringBuffer:适用于多线程下在字符串缓冲区进行大量操作。

2

String是final类不能被继承且为字符串常量,而StringBuilder和StringBuffer均为字符串变量。

3

1.StringBuilder(非线程安全)该类提供一个与StringBuffer兼容的 API,但不能保证同步,所以在性能上较高。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。两者的方法基本相同。2.StringBuffer(线程安全的)StringBuffer上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的点添加字符。

docker 部署

(1)下载yum源,用于安装docker引擎

(2)安装docker引擎

(3)查看版本信息[root@localhost yum.repos.d]# docker version(可以判定是否安装成功) [root@localhost ~]# docker pull nginx:1.19.8

4也可以通过传入压缩包的方式导入镜像到本地

docker load -i docker-mysql-5.7.tar.gz

(5)查看镜像 [root@localhost ~]# docker images

(6)运行该镜像

docker run -d nginx:1.19.8

使用git上传项目到仓库

1、添加操作: git add 文件名(将工作区新建/修改的内容添加到暂存区)

2、提交操作: git commit -m “commit message” 文件名(将暂存区的内容提交到本地库)

3、取回远程仓库的变化,并与本地分支合并

git pull 

4、上传本地指定分支到远程仓库

git push 

Arraylist跟LinekList的区别 ?

1 ArrayList是Array(动态数组)的数据结构,LinkedList是Link(链表)的数据结构。

2、效率不同

当随机访问List(get和set操作)时,ArrayList比LinkedList的效率更高,因为LinkedList是线性的数据存储方式,所以需要移动指针从前往后依次查找。

当对数据进行增加和删除的操作(add和remove操作)时,LinkedList比ArrayList的效率更高,因为ArrayList是数组,所以在其中进行增删操作时,会对操作点之后所有数据的下标索引造成影响,需要进行数据的移动。

3、自由性不同

ArrayList自由性较低,因为它需要手动的设置固定大小的容量,但是它的使用比较方便,只需要创建,然后添加数据,通过调用下标进行使用;而LinkedList自由性较高,能够动态的随数据量的变化而变化,但是它不便于使用。

4、主要控件开销不同

ArrayList主要控件开销在于需要在lList列表预留一定空间;而LinkList主要控件开销在于需要存储节点信息以及节点指针。

mybatis的一级缓存和二级缓存

一、什么是缓存

缓存是存在于内存中的临时数据。使用缓存减少和数据库的交互次数,提高执行效率。1、适用于缓存

经常查询并且不经常改变的;
数据的正确与否对最终结果影响不大的;

2、不适用于缓存

经常改变的数据;
数据的正确与否对最终结果影响很大的;
例如:商品的库存,银行的汇率,股市的牌价;

1、一级缓存简介

一级缓存作用域是sqlsession级别的,同一个sqlsession中执行相同的sql查询(相同的sql和参数),第一次会去查询数据库并写到缓存中,第二次从一级缓存中取。

一级缓存是基于 PerpetualCache 的 HashMap 本地缓存,默认打开一级缓存。2、何时清空一级缓存

如果中间sqlSession去执行commit操作(执行插入、更新、删除),则会清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。

一级缓存时执行commit,close,增删改等操作,就会清空当前的一级缓存;当对SqlSession执行更新操作(update、delete、insert)后并执行commit时,不仅清空其自身的一级缓存(执行更新操作的效果),也清空二级缓存(执行commit()的效果)。3、一级缓存无过期时间,只有生命周期

MyBatis在开启一个数据库会话时,会创建一个新的SqlSession对象,SqlSession对象中会有一个Executor对象,Executor对象中持有一个PerpetualCache对象,见下面代码。当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。三、mybatis二级缓存1、二级缓存简介

它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。

二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。2、二级缓存何时存入

在关闭sqlsession后(close),才会把该sqlsession一级缓存中的数据添加到namespace的二级缓存中。

开启了二级缓存后,还需要将要缓存的pojo实现Serializable接口,为了将缓存数据取出执行反序列化操作,因为二级缓存数据存储介质多种多样,不一定只存在内存中,有可能存在硬盘中。3、二级缓存有过期时间,但没有后台线程进行检测

需要注意的是,并不是key-value的过期时间,而是这个cache的过期时间,是flushInterval,意味着整个清空缓存cache,所以不需要后台线程去定时检测。

每当存取数据的时候,都有检测一下cache的生命时间,默认是1小时,如果这个cache存活了一个小时,那么将整个清空一下。

4、当 Mybatis 调用 Dao 层查询数据库时,先查询二级缓存,二级缓存中无对应数据,再去查询一级缓存,一级缓存中也没有,最后去数据库查找。

Redis分布式锁 MySQL聚簇索引

Redis分布式锁

分布式锁的引入:一个应用会部署多个进程,那这多个进程如果需要修改MySQL 中的同一行记录时,为了避免操作乱序导致数据错误,此时,我们就需要引入「分布式锁」来解决这个问题了。想要实现分布式锁,必须借助一个外部系统,所有进程都去这个系统上申请「加锁」,而这个外部系统,必须要实现「互斥」的能力,即两个请求同时进来,只会给一个进程返回成功,另一个返回失败(或等待)。这个外部系统,可以是MySQL,也可以是Redis 或Zookeeper。但为了追求更好的性能,我们通常会选择使用Redis或Zookeeper 来做。

分布式锁的特点

互斥性:保证在分布式部署的应用集群中,同一个方法在同一时间只能被一台机器上的一个线程执行。
可重入性:这把锁要是一把可重入锁(避免死锁),类似于ReentrantLock,同一服务节点的相同线程, 允许重复多次加锁;
阻塞性:支持阻塞和非阻塞: 和ReentrantLock 一样支持lock 和trylock 以及tryLock(long timeOut)。
高可用:有高可用的获取锁和释放锁功能
锁超时:支持锁超时,及时释放,防止锁死。
公平锁与非公平锁:公平锁是指按照请求加锁的顺序获得锁,非公平锁请求加锁是无序的。

分布式的实现方式

数据库
创建一张表, 通过对此表的insert/delete 达到获取, 释放锁的目的; 需要解决DB单点故障, 释放锁失败, 堵塞锁等情况;
优点:借助DB, 容易理解
缺点:性能开销大, 有些笨重, 可能导致系统越来越复杂
Redis
集群redis中通过setnx与expire, 或者setnx与getset设置锁的过期时间, 实现分布式锁;
优点: 有效解决单点问题, 性能高,实现起来较为方便;
缺点:setnx与expire无法确保事务, 一个客户端堵塞很久导致expire自动执行,
Redlock:是redis的作者设计的分布式锁方案
优点: •可用性高,大多数节点正常即可。 •单Redis 节点的分布式锁在
failover 时锁失效问题不复存在。
缺点: 设计比较复杂,个人实现比较麻烦,最好借用已经实现了的库
(Redission…)
Zookeeper
优点:有效的解决单点问题,不可重入问题,非阻塞问题以及锁无法释放的问题
实现较为简单
缺点:性能上不如使用缓存实现的分布式锁,因为每次在创建锁和释放锁的过程中,都要动态创建、销毁临时节点来实现锁功能需要对Zookeeper的原理有所了解。

MySQL聚簇索引

聚簇索引并不是一种单独的索引类型,而是一种数据存储方式

以一本英文课本为例,要找第8课,直接翻书,若先翻到第5课,则往后翻,再翻到第10课,则又往前翻。这本书本身就是一个索引,即“聚簇索引

1.题目:hashmap

答案:是map的子接口

  1. 如果数据小于等于8的时候,采用 数组 + 链表 的形式进行数据储存

  2. 如果数据超过8的时候,采用 数组 + 红黑树 进行储存

  3. 数组用于存放 key 所对应的 hashcode 值,链表 和 红黑树 用来存在 value 值

2.题目:堆、栈

答案: 堆内存 用于数据存储的()

​ 栈内存用于代码中方法执行

3.题目:java如何实现跨平台

答案: JVM实现的跨平台JVM对java文件编译成.class字节码文件后,会将生成对应版本(windows/linux/mac)的机器码给操作系统识别4.题目: docker镜像打包上传

5.题目:栈逃逸分析知不知道

答案:逃逸分析,是一种可以有效减少Java 程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法。通过逃逸分析,Java Hotspot编译器能够分析出一个新的对象的引用的使用范围从而决定是否要将这个对象分配到堆上(或者栈上)。Java在Java SE 6 以及以后的版本中支持并默认开启了逃逸分析的选项。Java的 HotSpot JIT编译器,能够在方法重载或者动态加载代码的时候对代码进行逃逸分析。逃逸分析的基本行为就是分析对象的动态作用域:当一个对象在方法中被定义后,它可能被外部方法所引用。方法逃逸:例如作为调用参数传递到其他方法中。线程逃逸:有可能被外部线程访问到,譬如赋值给类变量或可以在其他线程中访问的实例变量。经过逃逸分析之后,可以得到对象三种可能的逃逸状态:GlobalEscape(全局逃逸): 即一个对象的引用逃出了方法或者线程。例如,一个对象的引用是复制给了一个类变量,或者存储在在一个已经逃逸的对象当中,或者这个对象的引用作为方法的返回值返回给了调用方法。ArgEscape(参数级逃逸):即在方法调用过程当中传递对象的应用给一个方法。这种状态可以通过分析被调方法的二进制代码确定。NoEscape(没有逃逸):一个可以进行标量替换的对象。该对象可以不被分配在传统的堆上。编译器可以使用逃逸分析的结果,对程序进行优化:堆分配对象变成栈分配对象:一个方法当中的对象,对象的引用没有发生逃逸,那么这个方法可能会被分配在栈内存上而非常见的堆内存上。消除同步:线程同步的代价是相当高的,同步的后果是降低并发性和性能。逃逸分析可以判断出某个对象是否始终只被一个线程访问,如果只被一个线程访问,那么对该对象的同步操作就可以转化成没有同步保护的操作,这样就能大大提高并发程度和性能。矢量替代:逃逸分析方法如果发现对象的内存存储结构不需要连续进行的话,就可以将对象的部分甚至全部都保存在CPU寄存器内,这样能大大提高访问速度

6.题目:线程和线程池:

线程创建的方式:1.继承Thread类2.实现Runnable接口3.实现Callable接口4.线程池线程池:线程复用,控制最大并发数,管理线程​ 降低资源消耗​ 提高响应速度​ 提高线程可管理性

7.题目:lock和synchronaized区别

答案:1.synchronize是JVM层级,是java关键字,lock是API层级的锁

2.sync不用手动释放锁,在代码块执行完之后自动释放

lock需要手动释放锁,如果没有手动释放锁的话有可能出现死锁;(lock()和unlock())3.synchronized是不可以被中断,要么执行完成,要么抛异常 lock是可以被中断的,(1)设置超时trylock(Long timeout,TimeUnit unit);等待一段时间后如果还没有拿到锁就算了; (2)lockInterruptibly()放在代码块中,调用interrupt()方法可以中断锁4.sync 是非公平锁lock 可以是非公平锁,也可以是公平锁,默认非公平锁 new Reentrantlock(true)公平锁5.sync 在唤醒(notify 或者 notifyAll)线程时,只能随机唤醒一个或者唤醒全部线程lock可以精确唤醒某一个线程

8:题目:设计模式原则

答案: 单一职责原则(Single Responsibility Principle)

​ 接口隔离原则(Interface Segregation Principle)

依赖倒转原则(Dependency Inversion Principle)

​ 里氏替换原则

开闭原则(Open Closed Principle)

9.题目:CAP模式:

答案: A:高可用性​ C:数据一致性​ P:分区容错性这三个要素最多只能同时实现两点,不可能三者兼顾,

请列举你熟悉的设计模式

答:单例模式,spring aop里面的代理模式 工厂模式 适配器模式

.受检异常和非受检异常区别,请举例一个非受检异常

答:受检异常在编段必须做处理,要么使用throws关键字声明,要么使用try catch捕获

手写单列模式

1 饿汉式(线程安全)

/**

  • @title: Hunger 饿汉式(线程安全)

  • @@Description:

  • @Author sujiaying

  • @Date: 2021/7/29 12:47*/

public class Hunger {

private static Hunger instance  = new Hunger();
private Hunger(){}
public static Hunger getInstance(){
    return instance;
}

}

2 懒汉

/**

  • @title: Lazy 懒汉(线程不安全)加锁后安全

  • @@Description:

  • @Author sujiaying

  • @Date: 2021/7/29 12:51*/

public class Lazy {

private static Lazy instance;
private Lazy(){}
public static Lazy getInstance(){
    if(instance==null){
        instance = new Lazy();
    }
    return instance;
}

}

安全的懒汉式,线程安全

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
    public static synchronized Singleton getInstance() {  
        if (instance == null) {  
            instance = new Singleton();  
        }  
        return instance;  
    }  

3 CL双检锁/双重校验锁

/**

  • @title: DCL双检锁/双重校验锁(DCL,即 double-checked locking)线程安全,效率高

  • 区别于懒汉的加锁

  • dcl只锁创建单例,不锁获取单例,如果已经创建好了则不会加锁

  • @@Description:

  • @Author sujiaying

  • @Date: 2021/7/29 12:53*/

public class DCL {

private volatile static DCL instance;
private DCL(){}
public static DCL getInstance(){
    if(instance==null){
        synchronized (DCL.class){
            if(instance==null){
                instance = new DCL();
            }
        }
    }
    return instance;
}

}

4 静态内部类实现单例

public class Singleton {

private static class SingletonHolder {  
    private static final Singleton INSTANCE = new Singleton();  
}  
private Singleton (){}  
public static final Singleton getInstance() {  
    return SingletonHolder.INSTANCE;  
}  

}

5 枚举式单例

public enum Singleton {  
​
    INSTANCE;  
​
    public void whateverMethod() {  
​
    }  
​
}
​

linkedlist和arraylist的区别是什么

1、数据结构不同ArrayList是Array(动态数组)的数据结构,LinkedList是Link(链表)的数据结构。

2、效率不同当随机访问List(get和set操作)时,ArrayList比LinkedList的效率更高,因为LinkedList是线性的数据存储方式,所以需要移动指针从前往后依次查找。当对数据进行增加和删除的操作(add和remove操作)时,LinkedList比ArrayList的效率更高,因为ArrayList是数组,所以在其中进行增删操作时,会对操作点之后所有数据的下标索引造成影响,需要进行数据的移动

3、自由性不同

ArrayList自由性较低,因为它需要手动的设置固定大小的容量,但是它的使用比较方便,只需要创建,然后添加数据,通过调用下标进行使用;而LinkedList自由性较高,能够动态的随数据量的变化而变化,但是它不便于使用。

4、主要控件开销不同ArrayList主要控件开销在于需要在lList列表预留一定空间;而LinkList主要控件开销在于需要存储结点信息以及结点指针信息。

ArrayList 和 Vector 的区别是什么?

同步性:

Vector是线程安全的,也就是说它的方法直线是线程同步的,而ArrayList是线程不安全的,它的方法之间是线程不同步的

如果只有一个线程去访问集合那么使用ArrayList,他不考虑线程安全的问题,所以效率会高一些

如果是多个线程去访问集合,那么使用Vector

数据增长性:

ArrayList和Vector集合都有一个初始容量的大小,当元素的个数超过存储容量是,就需要增加ArrayList和Vector的存储空间,每次增加不是

增加一个而是增加多个,Vector是增加原来的两倍,ArrayList没有明文规定,但是从源码中可以看出增长原来的1.5倍

ArrayList和Vector可以设置初始的存储空间的大小,Vector还以设置增长空间大小,而ArrayList不可以。

list和map的区别是:

1、list是存储单列数据的集合,map是存储双列数据的集合;

2、list中存储的数据是有序的,map中存储的数据是无序的;

3、list允许重复,map的键不能重复,值可以重复

Bean生命周期

一阶段:Bean的实例化和DI(dependency injection)

1.1 扫描XML文件、注释类(例:@Component)、配置类中bean的定义(@Configuration -> @Bean)

1.2 创建Bean实例

1.3 注入Bean依赖项(调用setter或构造方法,为自动装配字段设置值)

二阶段:检查Spring Awareness

2.1 如果Bean实现了BeanNameAware接口,则调用setBeanName(...);

2.2 如果Bean实现了BeanClassLoaderAware接口,则调用setBeanClassLoader(...);

2.3 如果Bean实现了ApplicationContextAware接口,则调用setApplicationContext(...);

三阶段:创建bean生命周期回调

3.1 @PostConstruct注释,注释回调的方法上,1、2阶段Bean创建完毕即调用;

3.2 实现InitializingBean接口,调用afterPropertiesSet(...),1、2阶段Bean创建完毕即调用;

3.3 Bean定义中包含init-method(在XML中标签<bean>的属性)或@Bean(initMethod="...")指定的方法,1、2阶段Bean创建完毕即调用;

四阶段:销毁bean生命周期回调

4.1 @PreDestroy注释,注释回调方法上,销毁Bean之前调用;

4.2 实现DisposableBean接口,调用destroy(...),销毁Bean之前调用;

4.3 Bean定义中包含destroy-method(在XML中标签<bean>的属性)或@Bean(destroyMethod="...")指定的方法,销毁Bean之前调用;

说一说代理模式

Spring 提供了两种方式来生成代理对象: JDKProxy 和 Cglib,

具体使用哪种方式生成由 AopProxyFactory 根据 AdvisedSupport 对象的配置来决定。

区别:

默认的策略是如果目标类是接口,则使用 JDK 动态代理技术,否则使用 Cglib 来生成

代理。

JDK 动态代理:被代理类和代理类之间是兄弟关系,他们都实现了相同的接口,所以可

以使用父接口获取代理类对象,或者被代理类的类名首字母小写来获取代理类的对象。

Cglib 代理:被代理类和代理类之间是父子关系,代理类继承自被代理类。

mysql索引是

索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构

索引(index):好比书的目录,用于加快查找的效率;

索引的作用:加快查找效率.减慢插入和删除,修改效率.(需要同步调整索引结果)

MySQL目前主要有的索引类型为:普通索引、唯一索引、主键索引、组合索引、全文索引。

Primary Key 主键约束,自动创建唯一索引

Foreign Key 外键约束,外键字段的内容是引用另一表的字段内容,不能瞎写
Unique Index 唯一索引,唯一值但不是主键

mysql的引擎(了解)

一 、Innodb

支持事务,是事务安全的,提供行级锁与外键约束,有缓冲池,用于缓冲数据和索引。

适用场景:用于事务处理,具有ACID事物支持,应用于执行大量的insert和update操作的表。

二 、MyISAM

不支持事务,不支持外键约束,不支持行级锁,操作时需要锁定整张表,不过会保存表的行数,所以当执行select count(*) from tablename时执行特别快。

适用场景:用于管理非事务表,提供高速检索及全文检索能力,适用于有大量的select操作的表,如 日志表

三 、MEMORY

使用存在于内存中的内容创建表,每一个memory只实际对应一个磁盘文件。因为是存在内存中的,所以memory访问速度非常快,而且该引擎使用hash索引,可以一次定位,不需要像B树一样从根节点查找到支节点,所以精确查询时访问速度特别快,但是非精确查找时,比如like,这种范围查找,hash就起不到作用了。另外一旦服务关闭,表中的数据就会丢失,因为没有存到磁盘中。

适用场景:主要用于内容变化不频繁的表,或者作为中间的查找表。对表的更新要谨慎因为数据没有被写入到磁盘中,服务关闭前要考虑好数据的存储

四、 MERGE

MERGE存储引擎把一组MyISAM数据表当做一个逻辑单元来对待,让我们可以同时对他们进行查询。构成一个MERGE数据表结构的各成员MyISAM数据表必须具有完全一样的结构。每一个成员数据表的数据列必须按照同样的顺序定义同样的名字和类型,索引也必须按照同样的顺序和同样的方式定义。

除了便于同时引用多个数据表而无需发出多条查询,MERGE数据表还提供了以下一些便利。

MERGE数据表可以用来创建一个尺寸超过各个MyISAM数据表所允许的最大长度逻辑单元

你看一把经过压缩的数据表包括到MERGE数据表里。比如说,在某一年结束之后,你应该不会再往相应的日志文件里添加记录,所以你可以用myisampack工具压缩它以节省空间,而MERGE数据表仍可以像往常那样工作

jvm垃圾回收机制

一、垃圾回收原理

GC (Garbage Collection:即垃圾回收)的基本原理:将内存中不再被使用的对象进行回收,GC中用于回收的方法称为收集器,由于GC需要消耗一些资源和时间,Java在对对象的生命周期特征进行分析后,按照新生代、老年代的方式来对对象进行收集,以尽可能的缩短GC对应用造成的暂停● 对新生代的对象的收集称为minor GC● 对老年代的对象的收集称为Full GC● 程序中主动调用System.gc()强制执行的GC为Full GC

不同的对象引用类型, GC会采用不同的方法进行回收,JVM对象的引用分为了四种类型:● 强引用:默认情况下,对象采用的均为强引用(这个对象的实例没有其他对象引用,GC时才会被回收)● 软引用:软引用是Java中提供的一种比较适合于缓存场景的应用(只有在内存不够用的情况下才会被GC)● 弱引用:在GC时一定会被GC回收● 虚引用:由于虚引用只是用来得知对象是否被GC

spring怎么控制事务

1、前置通知@Before:在目标方法执行之前执行2、后置通知/后置返回通知:在目标方法调用之后执行3、异常通知/后置异常通知@AfterThrowing:在调用目标方法出现异常时执行4、最终通知:调用目标方法不管是否出现异常,都会执行,相当于finally中的代码----------以上四种通知无法阻止目标方法调用,目标方法是由Spring来控制----------5、环绕通知:是Spring给我们提供的一种手动调用目标对象方法或者其他通知的通知方式

mybaits-plus的方法

增 insert

删 delete

改 update

查 select

分页 selectPage

SpringMVC工作流程

  1. 用户通过浏览器发起 HttpRequest 请求到前端控制器 (DispatcherServlet)。

  2. DispatcherServlet 将用户请求发送给处理器映射器 (HandlerMapping)。

  3. 处理器映射器 (HandlerMapping)会根据请求,找到负责处理该请求的处理器,并将其封装为处理器执行链 返回 (HandlerExecutionChain) 给前端控制器 DispatcherServlet

  4. DispatcherServlet前端控制器 会根据 处理器执行链 中的处理器,找到能够执行该处理器的处理器适配器(HandlerAdaptor) --注,处理器适配器有多个

  5. 处理器适配器 (HandlerAdaptoer) 会调用对应的具体的 Controller

  6. Controller 将处理结果及要跳转的视图封装到一个对象 ModelAndView 中并将其返回给处理器适配器 (HandlerAdaptor)

  7. HandlerAdaptor 直接将 ModelAndView 交给 DispatcherServlet ,至此,业务处理完毕

  8. 业务处理完毕后,我们需要将处理结果展示给用户。于是DisptcherServlet 调用 ViewResolver,将 ModelAndView 中的视图名称封装为视图对象

  9. ViewResolver 将封装好的视图 (View) 对象返回给 DIspatcherServlet

  10. DispatcherServlet前端控制器 调用视图对象,让其自己 (View) 进行渲染(将模型数据填充至视图中),形成响应对象 (HttpResponse)

  11. 前端控制器 (DispatcherServlet) 响应 (HttpResponse) 给浏览器,展示在页面上

Spring框架七大核心模块

  1. 容器模块(spring core)

这是Spring框架最基础的部分,它提供了依赖注入(DependencyInjection)特征来实现容器对Bean的管理。核心容器的主要组件是 BeanFactory,BeanFactory是工厂模式的一个实现,是任何Spring应用的核心。它使用IoC将应用配置和依赖从实际的应用代码中分离出来

  1. 应用上下文模块(spring context)

    核心模块的BeanFactory使Spring成为一个容器,而上下文模块使它成为一个框架。这个模块扩展了BeanFactory的概念,增加了对国际化(I18N)消息、事件传播、验证的支持这个模块提供了许多企业服务,例如电子邮件、JNDI访问、EJB集成、远程以及时序调度(scheduling)服务。也包括了对模版框架例如Velocity和FreeMarker集成的支持

  2. AOP模块(spring aop)

Spring在它的AOP模块中提供了对面向切面编程的丰富支持,Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中,可以自定义拦截器、切点、日志等操作

  1. JDBC抽象和DAO模块(spring dao)

提供了一个JDBC的抽象层和异常层次结构,消除了烦琐的JDBC编码和数据库厂商特有的错误代码解析, 用于简化JDBC

  1. 对象/关系映射集成模块(spring orm)

Spring提供了ORM模块。Spring并不试图实现它自己的ORM解决方案,而是为几种流行的ORM框架提供了集成方案,包括Hibernate、JDO和iBATIS SQL映射,这些都遵从 Spring 的通用事务和 DAO 异常层次结构

  1. Web模块(spring web)

Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文,提供了Spring和其它Web框架的集成,比如Struts、WebWork。还提供了一些面向服务支持,例如:实现文件上传的multipart请求

  1. MVC模块(spring mvc)

Spring为构建Web应用提供了一个功能全面的MVC框架。虽然Spring可以很容易地与其它MVC框架集成,例如Struts,但Spring的MVC框架使用IoC对控制逻辑和业务对象提供了完全的分离

谈谈你对 Spring 的理解?

1.狭义上 spirng 就是指 Spring Framework,特别是其中的核心思想控制反转和依赖注入、事务处理等特性,在进行大型项目开发的时候能够帮助我们更好的管理对象。2.广义上 spring 就是指 spring 家族的一些列产品:如 SpringMvc,SpringBoot,SpringJPA,SpringCloud 等等。Spring 是项目开发中最重要的框架,可以帮助我们更好的管理对象,减少依赖,提高开发效率。

18.Spring 中体现的设计模式?

工厂设计模式 : Spring 使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。代理设计模式 : Spring AOP 功能的实现。单例设计模式 : Spring 中的 Bean 默认都是单例的。模板方法模式 : Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。包装器设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。适配器模式 :Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controlle

如何理解springboot框架

Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。

  1. j简单来说

  2. 服务于spring框架的框架

  3. 约定优于配置下的产物

一.四大组件

1.auto-configuration 自动装配2.starter 模块启动器在pom.xml中引入相关启动器 就可以使用对应的模块功能3.springboot cli一个命令行使用Spring Boot的客户端工具4.actuatoractuator是Spring Boot的监控插件,本身提供了很多接口可以获取当前项目的各项运行状态指标。

二.springboot的特点:

@SpringBootApplication 项目启动注解

@SpringBootConfiguration  初始化配置
@Configuration 表示配置类

@EnableAutoConfiguration 自动装配注解

一、int 互转 string

一、String转为int

int i=Integer.parseInt(string);
int i=Integer.valueOf(s).intValue();

二、int转为String

String s = String.valueOf(i);
String s = Integer.toString(i);
String s = “” + i;

Java 多线程中调用 wait()和 sleep()方法有什么不同?

Java 程序中 wait 和 sleep 都会造成某种形式的暂停,它们可以满足不同 的需要。wait()

方法用于线程间通信,如果等待条件为真且其它线程被唤醒时它 会释放锁,而 sleep()方法

仅仅释放 CPU 资源或者让当前线程停止执行一段时 间,但不会释放锁。

java实现同步的几种方式

1、同步方法

2.同步代码块

3.使用特殊域变量(volatile)实现线程同步

4.使用重入锁实现线程同步

(5)使用局部变量实现线程同步

jdk1.8新特性

  • Lambda表达式

  • 函数式接口

  • *方法引用和构造器调用

  • Stream API

  • 接口中的默认方法和静态方法

  • 新时间日期API

创建线程三种方式的对比

1、采用实现 Runnable、Callable 接口的方式创建多线程。

优势是:

线程类只是实现了 Runnable 接口或 Callable 接口,还可以继承其他类。

在这种方式下,多个线程可以共享同一个 target 对象,所以非常适合多个相同线程来

处理同一份资源的情况,从而可以将 CPU、代码和数据分开,形成清晰的模型,较好地体

现了面向对象的思想。

劣势是:

编程稍微复杂,如果要访问当前线程,则必须使用 Thread.currentThread()方法。

2、使用继承 Thread 类的方式创建多线程

优势是:

编写简单,如果需要访问当前线程,则无需使用 Thread.currentThread()方法,直接使用

this 即可获得当前线程。

劣势是:

线程类已经继承了 Thread 类,所以不能再继承其他父类。

3、Runnable 和 Callable 的区别

1、Callable 规定(重写)的方法是 call(),Runnable 规定(重写)的方法是 run()。

2、Callable 的任务执行后可返回值,而 Runnable 的任务是不能返回值的。

3、Call 方法可以抛出异常,run 方法不可以。

4、运行 Callable 任务可以拿到一个 Future 对象,表示异步计算的结果。它提供了检

查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过 Future 对象可以了

解任务执行情况,可取消任务的执行,还可获取执行结果。

Linux常用查看端口号命令

1 lsof(list open files)是一个列出当前系统打开文件的工具。

lsof -i:端口号

2

netstat -nltp | grep 端口号

怎么建立的接口是好的接口

(1)易学(2)易用,甚至不需要文档(3)难于误用(4)容易阅读与维护(5)容易扩展

.什么是 redis,为什么要使用?

redis 是一个 NoSQL 的数据库,是一个内存级别的数据库,内部存储是以 key,value 形式进行数据存储的,它支持的数据类型也比较多,String(字符串)、List(列表可重复集合)、Hash(Map

集合)、Set(无序不可重复集合)、zset(有序不可重复集合)。其中应用场景如下:

String:JSON 数据存储;

List:消息队列,限时抢购,秒杀;

Hash:存储用户信息(单点登录)

Set:1.利用交集求共同的好友。2.利用唯一性,可以统计访问网站的所有独立 IP

zSet:可以用于积分排行榜

使用 redis 的原因呢,因为我们是一个分布式的项目,访问非常的频繁,数据改动稍微少一

点的信息时,这个时候我们就会使用 redis,因为 MySQL 是将信息读写到磁盘中,读写都是

涉及到 IO 流,这样效率就比较低,而 redis 是一个内存级别的非关系型数据库,它可以很好

地满足当前的互联网项目对于高并发的处理需求

说说 redis 的缓存雪崩,缓存击穿,缓存穿透

缓存雪崩

就是缓存在同一时间大面积失效导致的请求直接去访问数据库,给数据库造成过

大压力,数据连接出现异常。

解决方法呢,就是给缓存设置不同的有效期,最有效的办法还是搭建集群,缓存失效后,可

以通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个 key 只允许一个线程查

询数据和写缓存,其他线程等待,不同的 key,设置不同的过期时间,让缓存失效的时间点

尽量均匀一些。

缓存击穿

就是指缓存中没有的数据但是数据库中有,这时候由于并发量过多,去缓存中没

有读到数据,同时又跑去数据库中读取,引起数据库瞬间压力过大。其实说白了就是指并发

差同一条数据,缓存雪崩是不同的数据都过期了,很多数据都查不到从而去数据库查。

解决方法呢,可以添加一个互斥锁,如果缓存不存在的话,就获取锁,获取到锁以后去读取数据库,如果没有获取到锁就等待缓存中有数据智慧化再去读取,说白了就是 sleep。

缓存穿透

,就是说去查询一个缓存和数据库中都没有的数据,正常使用缓存的流程应该是,

数据查询先进性缓存查询,如果 key 不存在或者已经过期的话再去数据库中查询,并把查询

到的对象放进缓存中,如果查询数据库也为空就不放入缓存。如果查询一个根本不存在的

key,就必然会每次都去查询数据库,而且每次的查询结果都是空,每次都不会进行缓存,

所以我觉得这种情况一般是恶意攻击,可能会利用这个漏洞对数据库造成压力,甚至会压垮

数据库。

这个怎么解决呢,接口一定要做好校验,也可以将 key 存为 key-null

Mybatis 的一级、二级缓存:

Mybatis 中有一级缓存和二级缓存,默认情况下一级缓存是开启的,而且是不能关闭的。一

级缓存是指 SqlSession 级别的缓存,当在同一个 SqlSession 中进行相同的 SQL 语句查询

时,第二次以后的查询不会从数据库查询,而是直接从缓存中获取,一级缓存最多缓存 1024

条 SQL。二级缓存是指可以跨 SqlSession 的缓存。是 mapper 级别的缓存,对于 mapper 级

别的缓存不同的 sqlsession 是可以共享的。

二级缓存具体使用需要配置:

  1. Mybatis 全局配置中启用二级缓存配置

  2. 在对应的 Mapper.xml 中配置 cache 节点

  3. 在对应的 select 查询节点中添加 useCache=true

MyBatis 实现一对一,一对多有几种方式?具体是怎么实现的?

一对一:

有联合查询和嵌套查询,联合查询是几个表联合查询,只查询一次, 通过在 resultMap 里面配

置 association 节点配置一对一的类就可以完成;嵌套查询是先查一个表,根据这个表里面

的结果的 外键 id,去再另外一个表里面查询数据,也是通过 association 配置,但另外一个

表的查询通过 select 属性配置。

一对多:

有联合查询和嵌套查询。联合查询是几个表联合查询,只查询一次,通过在 resultMap 里面的

collection 节点配置一对多的类就可以完成;嵌套查询是先查一个表,根据这个表里面的 结

果的外键 id,去再另外一个表里面查询数据,也是通过配置 collection,但另外一个表的查询通

过 select 节点配置。

抽象类和接口的区别

1.抽象类使用abstract修饰,接口使用interface修饰
2.抽象类可以有构造方法,接口中不能有构造方法。
3.抽象类中可以有普通成员变量,接口中没有普通成员变量
4.抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。
5.抽象类中可以包含静态方法,接口中不能包含静态方法
6.抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,
  但接口中定义的变量只能是public static final类型,并且默认即为public static final类型
7.一个类可以实现多个接口,但只能继承一个抽象类。

8.JDBC的步骤:

1.注册驱动 
  Class.forName("com.mysql.jdbc.cj.Driver");
2.连接数据库
  String url= "jdbc:mysql://localhost:3306/cgb2104?characterEncoding=utf8";
  Connection conn = DriverManager.getConnection(url, "root","root");
3.获取传输器
  传输器一:Statement()传输器,有sql注入风险。
  传输器二:prepareStatement(sql); 先把SQL骨架发给数据库执行,给sql里的占位符?设置参数
​
4.执行sql---获取结果集
5.解析结果集
6.释放资源

数据库的约束

主键约束 primary key  auto_increment(主键自增)  
   主键是一条记录的唯一标识,具有唯一性,不能重复
外键约束 Foreign Key
   外键字段的内容是引用另一表的字段内容
唯一约束 unique  唯一值但不是主键
非空约束 not null 字段不可为空,但可以重复
检查约束 check  未字段设置条件,超出条件会报错
默认约束 default 为字段设置默认值

数据库关键字执行顺序

(1) FROM [left_table] 选择表


(2) ON <join_condition> 链接条件


(3) <join_type> JOIN <right_table> 链接


(4) WHERE <where_condition> 条件过滤


(5) GROUP BY <group_by_list> 分组


(6) AGG_FUNC(column or expression),... 聚合


(7) HAVING <having_condition> 分组过滤


(8) SELECT (9) DISTINCT column,... 选择字段、去重


(9) ORDER BY <order_by_list> 排序


(10) LIMIT count OFFSET count; 分页

反向代理跟正向代理区别

正向代理代理的是客户端,帮客户端发送请求给目标服务器,服务器响应后,由代理服务器将响应内容返回给客户端。使客户端对目标服务器不可见。比较常见的用户场景就是fān qiáng

反向代理在生产中应用还是比较多的,代理服务器代理目标服务器,负责收发请求,目标服务器对客户端来说是不可见的,不过客户端请求目标服务器和请求代理是一样的效果。比较常见的场景就是nginx的负载均衡

orm思想是什么

是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值