JAVA面试知识点

目录

JAVA面试知识点梳理... 1

Java基础... 1

JavaWeb. 11

前端部分... 13

数据库... 13

Linux. 14

java设计模式... 15

JVM虚拟机... 15

各家公司的面试题... 16

JAVA面试知识点梳理

Java基础

Java核心技术

==与equals的区别

==判断两个对象的内存地址(引用)是否相等,判断的是两个变量和实例是不是指向同一个内存空间,“==”除了比较基本数据之外都是用来比较内存地址。

Equals(),在没有重写equals方法时(object的equals方法)等价于==,重写后判断的是两个变量和实例所指向的内存空间的值是不是相同,对字符串的内容进行比较。

String,StringBuffer, StringBuilder 的区别是什么?

String是字符串常量,StringBuffer和StringBuilder是字符串变量。StringBuffer是线程安全的,StringBuilder是非线程安全的。具体来说String是一个不可变的对象,每次修改String对象实际上是创建新对象,并将引用指向新对象,效率很低。StringBuffer是可变的,即每次修改只是针对其本身,大部分情况下比String效率高,StringBuffer保证同步(synchronized),所以线程安全。StringBuilder没有实现同步,所以非线程安全。但效率应该比StringBuffer高。StringBuffer使用时最好指定容量,这样会比不指定容量快30%-40%,甚至比不指定容量的StringBuilder还快。注:String用“+”连接,StringBuffer用“append()”连接。

ArrayList和Vector的区别?

  1. 同步性:Vector是线程安全的,也就是说它的方法间是线程同步的,而ArrayList是线程不安全的,它的方法之间是线程不同步的,因此,当只有一个线程会访问到集合时,ArrayList的效率会更高;而当有多个线程访问集合时,使用Vector的好处是不需要自己编写线程安全的代码。
  2. 数据增长:ArrayList和Vector都有一个初始的容量大小,当欲存储的元素个数超过了容量时,就需要增加ArrayList和Vector的存储空间,Vector增加原来的一倍,ArrayList增加原来的0.5倍。

Java中对象的地址和引用概念?

1、对象的地址:ObjecthashCode()默认是返回内存地址,请看下边程序,

public static void main(String[] args) {

    String str1 = new String("abc");

    String str2 = new String("abc");

    System.out.println("str1 hashCode: " + str1.hashCode());

    System.out.println("str2 hashCode: " + str2.hashCode());

    System.out.println("str1 identityHashCode: " + System.identityHashCode(str1));

    System.out.println("str2 identityHashCode: " + System.identityHashCode(str2));

    User user = new User("test", 1);

    System.out.println("user hashCode: " + user.hashCode());

    System.out.println("user identityHashCode: " + System.identityHashCode(user));

}

输出结果

str1 hashCode: 96354

str2 hashCode: 96354

str1 identityHashCode: 1173230247

str2 identityHashCode: 856419764

user hashCode: 621009875

user identityHashCode: 621009875

String类重写了hashCode方法,源码如下

public int hashCode() {

    int h = hash; // 默认值是0

    if (h == 0 && value.length > 0) {

        char val[] = value; // 字符串对应的char数组

        for (int i = 0; i < value.length; i++) {

            h = 31 * h + val[i]; // val[0]*31^(n-1) + val[1]*31^(n-2) + ... + val[n-1]

        }

        hash = h;

    }

    return h;
}

根据String的值来确定hashCode的值,所以只要值一样,导致String str1和str2的hashCode就会一样。identityHashCode永远返回根据对象物理内存地址产生的hash值,所以identityHashCode会不一样。

User user=new User(“test”,1);User对象没有重写hashCode方法,所以hashCode和identityHashCode返回的值一样。

2、引用分为强引用、软引用、弱引用和虚引用。

例如:Hero h=new Hero();

h为Hero类型,称为引用,代表右边创建的对象,又叫做“指向”。

强引用:当内存空间不足时,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题,通常new一个对象为强引用。

软引用:如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。应用场景:浏览器后退。

弱引用:不管当前内存空间足够与否,都会回收弱引用的内存。弱引用和软引用都可以和引用队列一起使用。

虚引用:就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。主要用来跟踪对象被垃圾回收器回收的活动。

区分对象和对象变量

new Date(),定义对象

Date deadline;定义对象变量deadline。

实验中,按照以下程序运行未得到期望的结果,

#include <stdio.h>

char *getStr(void){

                   char p[]="hellow world";

                   return p;

                 }

void test(void){

         char *str=NULL;

         str=getStr();

         printf(str);

}

补充C语言有关指针的例子:

写出一个指向函数的指针,该函数有一个整型参数并返回一个整型数?

一个有10个指针的数组:*a[10];

该指针指向一个函数:(*a[10])();

该函数有一个整形参数: (*a[10])(int);

并返回一个整型数: int (*a[10])(int)

最终答案:int (*a[10])(int)

静态方法和非静态方法的区别是什么

总结有如下区别:

1、静态方法属于类所有,类实例化前即可使用;

2、非静态方法可以访问类中的任何成员,静态方法只能访问类中的静态成员;

3、因为静态方法在类实例化前就可以使用,而类中的非静态变量必须在实例化之后才能分配内存;   

4、static内部只能出现static变量和其他static方法!而且static方法中还不能使用this等关键字,因为它是属于整个类;

5、静态方法效率上要比实例化高,静态方法的缺点是不自动进行销毁,而实例化的则可以做销毁;

6、静态方法和静态变量创建后始终使用同一块内存,而使用实例的方式会创建多个内存。

主要区别:静态方法在创建对象前就可以使用了,非静态方法必须通过new出来的对象调用。

静态方法与实例方法在性能和占用内存上没有明显的区别,是否声明为静态方法需要从类型的非静态字段、事件、面向对象扩展和多态这三方面来考虑。

静态变量和实例变量的区别?

答:静态变量是被static修饰符修饰的变量,也称为类变量,它属于类,不属于类的任何一个对象,一个类不管创建多少个对象,静态变量在内存中有且仅有一个拷贝;实例变量必须依存于某一实例,需要先创建对象然后通过对象才能访问到它。静态变量可以实现让多个对象共享内存。在Java开发中,上下文类和工具类中通常会有大量的静态成员。

是否可以从一个静态(static)方法内部发出对非静态(non-static)方法的调用?

答:不可以,静态方法只能访问静态成员,因为非静态方法的调用要先创建对象,因此在调用静态方法时可能对象并没有被初始化。

Java中的内存分配(堆、栈和常量池)

堆是一个运行时数据区,优势在于可以动态地分配内存大小,堆内存用来存放由new创建的对象和数组,它是JVM中最大的,分为新生代、年老代和永久代三部分。

栈的优势是存取速度比堆快,仅次于寄存器,栈数据可以共享。

常量池是在编译期确定,并被保存在已编译的.class文件中的一些数据。在程序执行中,常量池会存在Method Area,而不是堆中。

栈中存放基础数据类型的对象和自定义对象的引用,自定义对象存放在堆中。

每个线程都有自己的栈内存,一个线程中存储的变量是其他线程不可见的,而堆是所有线程可见的公共内存空间。

构造器(constructor)是否可被重写(override)?

答:构造器不能被继承,因此不能被重写,但可以被重载。

什么是多态?

多态存在的三个必要条件:继承、重写、父类引用指向子类对象及向上转型(如Parent p=new Child();)

虚函数的存在是为了多态,虚函数属于c++概念,加final转非虚函数。

动态绑定是 Java的默认行为。

运算符优先级

   优先级1:

()

::

[]

.

*

优先级2:

^

一元运算符++  --

二元运算符+  -

优先级3:

* /%+-<<

移位操作符:>>、 << 、<<<

右边的参数要先进行模的32位运算,并且移位是对二进制的操作,8位一个循环,如num>>32等于num>>0,而num>>33等于num>>1

泛型

反射

数据结构

float型float f=3.4是否正确?

答案:不正确。

原因:因为3.4是双精度的,下转型成浮点型,应该用强制类型转换, float f=(float)3.4 或float f = 3.4F 。

注:在java里面,没小数点的默认是int,有小数点的默认是 double

short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗?

答:对于short s1 = 1; s1 = s1 + 1;由于1是int类型,因此s1+1运算结果也是int 型,需要强制转换类型才能赋值给short型。而short s1 = 1; s1 += 1;可以正确编译,因为s1+= 1;相当于s1 = (short)(s1 + 1);其中有隐含的强制类型转换。

解释内存中的栈(stack)、堆(heap)和静态存储区的用法。

答:通常我们定义一个基本数据类型的变量,一个对象的引用,还有就是函数调用的现场保存都使用内存中的栈空间;而通过new关键字和构造器创建的对象放在堆空间;程序中的字面量(literal)如直接书写的100、“hello”和常量都是放在静态存储区中。栈空间操作最快但是也很小,通常大量的对象都是放在堆空间,整个内存包括硬盘上的虚拟内存都可以被当成堆空间来使用。

String str = new String(“hello”);

上面的语句中str放在栈上,用new创建出来的字符串对象放在堆上,而“hello”这个字面量放在静态存储区。

用最有效率的方法计算2乘以8?

答:因为将一个数左移n 位,就相当于乘以了2 的n 次方。

2 << 3(左移3位相当于乘以2的3次方,右移3位相当于除以2的3次方)。

a << b的值实际上就是a乘以2的b次方。

集合类

多线程

线程:是操作系统能够运算调度的最小单位。可以使用多线程对计算密集型任务提速,如一个线程完成一个任务需要100ms,用十个线程处理该任务则只需10ms。

阻塞和非阻塞算法

阻塞/非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态。

阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。

非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。

***同步和异步

同步交互:指发送一个请求,需要等待返回,然后才能够发送下一个请求,有个等待过程;

异步交互:指发送一个请求,不需要等待返回,随时可以再发送下一个请求,即不需要等待。

注:而在通讯中的同步通信和异步通信概念,同步一般比异步通讯快,而且开销少, 异步传输常用于低速设备。异步传输是面向字符的,其单位是字符,同步传输是面向比特的,其单位是帧。

多线程或者多进程方案:

多进程:每到达一个请求,为这个请求创建一个进程,

多线程:为每个请求新建一个线程进行处理,所有的线程都共享同一个进程空间。

一个阻塞算法保证一个共享数据结构的行为

非阻塞并发算法分两步:

  1. 执行线程请求的操作;
  2. 通知请求线程操作不能被执行。

一个非阻塞并发算法保证共享数据结构的行为

网络编程

JavaWeb

Servlet

Servlet生命周期

主要有三个方法:init()、service()和destroy()

请求转发

请求转发与重定向

JSP

JSTL

Spring

Springmvc

springMVC

dispatcher

注解:@ResponseBody

Mybatis

Hibernate和Mybatis区别

Hibernate强大,方便高效,复杂,绕弯子,全自动

Mybatis性能要求更高,响应更快,更灵活,半自动。

Springboot

前端部分

JavaScript

数据库

select * from table order by id limit m, n;

select * from table where id > #max_id# order by id limit n;

select * from table where id > #max_id# order by id limit 10, 10;

select * from table as a inner join (select id from table order by id limit m, n) as b on a.id = b.id order by a.id;

select * from table where id > (select id from table order by id limit m, 1) limit/*

* mysql数据库

* firstIndex:起始的索引

* pageSize:每页显示的记录数

*/

select o.* from (sql) o limit firstIndex,pageSize

/*

*SQLServer数据库

* firstIndex:起始索引

* pageSize:每页显示的数量

* orderColumn:排序的字段名

* sql:可以是简单的单表查询语句,也可以是复杂的多表联合查询语句

*/

select top pageSize o.* from (select row_number() over(order by orderColumn) as rownumber,* from(sql) as o where rownumber>firstIndex;

/*

*Oracle数据库

*/

select * from(select * from(select t.*,row_number() over(order by orderColumn) as rownumber from(sql) t) p where p.rownumber>firstIndex) where rownum<=pageSize

Linux

Linux的基本命令

find [PATH] [option] [action]

ls -a

cp

mv

rm

tar

mkdir

java设计模式

设计模式

经常被问到,要熟记至少3种简单的设计模式,工厂模式、单例模式。

单例模式:在单例类的内部实现只生成一个实例,同时它提供一个静态的getInstance()工厂方法,让客户可以访问它的唯一实例;为了防止在外部对其实例化,将其构造函数设计为私有;在单例类内部定义了一个Singleton类型的静态对象,作为外部共享的唯一实例。

JVM虚拟机

获取Class的三种方式:

1.通过类名获取      类名.class   

  2.通过对象获取      对象名.getClass()

3.通过全类名获取    Class.forName(全类名)

各家公司的面试题

远图互联科技股份有限公司

Netty

是一个异步事件驱动的网络应用程序框架。

Netty特点:高性能(高并发)、异步事件驱动的NIO框架,提供的socket底层支持对TCP、UDP和文件传输。

高性能体现在:

IO线程模型:同步非阻塞,用最少的资源做更多的事

内存零拷贝:尽量减少不必要的内存拷贝

内存池设计:申请的内存可以重用,主要指直接内存

串行化处理读写:避免使用锁带来的性能开销

高性能序列化协议:支持protobuf等高性能序列化协议

扩展:BIO、NIO和AIO的区别?

BIO:同步并阻塞,一个连接一个线程,客户端有连接请求时,服务器就需要启动一个线程进行处理。适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高。

NIO(Nonblocking IO):同步非阻塞,一个请求对应一个处理线程。适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂。

BIO和NIO比较,使用BIO的时候往往会引入多线程,每个连接一个单独的线程;NIO则是使用单线程或者只使用少量的多线程,多个连接共用一个线程。

AIO:异步非阻塞,使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂。

WebSocket

一种允许服务器向客户端传递信息,实现浏览器和客户端双工通信的新协议。

特点:

1、与Http协议有良好的兼容性;

建立在TCP协议之上,属于应用层;

数据格式比较轻量,性能开销小,通信高效;

可以发送文本,也可以发送二进制;

没有同源限制,可以与任意服务器通信。

Http协议是短连接,请求之后就会关闭连接,

webSocket协议是长连接,一次请求来初始化连接,然后所有请求和响应都是通过TCP连接进行通信。

对比webSocket,socket是一组接口。

粘包和拆包问题(仅针对于TCP传输

拆包原因:一次发送的数据大于套接字缓冲区;报文长度大于分段长度。

粘包原因:一次发送数据小于套接字缓冲区大小,网卡将多次发送的数据一次性发送;不及时读取缓冲区数据。

注:报文是网络中交换与传输的数据单元,即站点一次性要发送的数据块。分段长度->MSS(最大分段大小)是TCP数据包每次传输的最大数据分段大小。

解决办法:

  1. 发送端给每个包添加首部,包含长度信息;
  2. 每个包封装为固定长度;
  3. 可以用特殊字符在数据包之间设置边界。

***HashMap

存储结构为数组+链表+红黑树(JDK1.8增加红黑树部分)

由于Hash函数难免出现index冲突的情况,当新来的Entry映射到冲突的数组位置时,只需要插入到对应的链表即可。

Put方法

Get方法

HashMap最多只允许一条记录的键为null,允许多条记录的值为null。

默认的初始长度:16

注:红黑树(R-B Tree)是一种特殊的二叉查找树

特性之一:如果一个节点是红色的,则它的子节点必须是黑色的;从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。

关键性质:从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。这一性质保证了树的大体是平衡的。时间复杂度为O(lgn);存储有序数据,例子有Java中的TreeSet和TreeMap

添加和删除基本操作都会用到旋转

HashMap和HashTable区别

主要从线程安全性、同步(Synchronization)以及速度比较

HashMap是非同步的,为了避免死锁,要自己添加同步功能。

HashTable是同步的,意味着多个线程可以共享一个HashTable,而在Java5及以上,HashTable的替代方案是ConcurrentHashMap

另外,HashTable不允许null值(Key和Value都不可以)

HashMap允许使用null值。

初始化和扩容方式不同

HashTable默认的初始大小为11,每次扩充容量变为原来的2n+1;HashMap默认的初始大小为16,每次扩充容量变为原来的2倍。

遍历方法不同:HashTable使用Enumeration遍历,而HashMap使用Iterator(fail-fast迭代器)进行遍历。

中科软面试题:

Jsp中调用后端的方式是怎样的?

前后端交互技术有form表单提交、Ajax请求和Servlet技术。

例如以下几种方案:

Form表单提交+SpringMVC

Form表单提交+JS的Ajax技术

vue+axios的表单提交

Form表单提交+Servlet(doGet/doPost)

Ajax同步和异步概念?

参照上边分析的通讯同步和异步。

同步=顺序处理

异步=并行处理

Ajax中的布尔参数async,选择JS代码是否等待ajax返回结果再执行。

Spring和MyBatis框架掌握的怎么样?

关于Spring:

集成有这些模块:核心容器、数据访问/集成、Web、AOP(面向切面编程)、消息和测试模块。

6个特征:轻量、控制反转、面向切面、容器、框架和MVC。

Spring Core

Spring Aspect

Spring Aop

Spring JDBC

Spring JMS

Spring ORM

Spring WEB

Spring Test

IOC思想是原本由在程序中手动创建对象的控制权,交由Spring框架来管理。由IOC容器完成对象的注入。

Spring有哪些注入方式?

有五种:

  1. 常用的@Autowired:自动装配;通过将@Autowired注解放在方法上来完成方法参数注入;
  2. Setter方法注入:在SpringAction定义一个private的SpringDao成员变量,然后创建SpringDao的set方法。配置文件Bean中配property
  3. 构造器注入:指带有参数的构造函数注入。配置文件Bean中配constructor-arg
  4. 静态工厂的方法注入:类中代码与第一种方式一样,配置文件中设置配factory-method属性
  5. 实例工厂的方法注入:配置文件中添加factory-bean、factory-method属性。

Spring AOP和AspectJ AOP区别?

Spring AOP是运行时增强,基于代理(proxying);AspectJ AOP是编译时增强,基于字节码操作(Bytecode Manipulation)。AspectJ AOP功能较强,适合切面较多的场景。

关于Mybatis(MyBatis-plus)

作为半ORM(对象关系映射)框架,内部封装了JDBC,加载驱动、创建连接、创建statement等繁杂的过程,开发者开发时只需要关注如何编写SQL语句。SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用。

可以通过XML 或注解来配置和映射原生信息,注解有

@MapperScan: 扫描某个包目录下的Mapper,将Mapper接口类交给Spring进行管理

@Mapper: 为了不再写mapper映射文件,告诉Spring框架此接口的实现类由Mybatis负责创建,并将其实现类对象存储到spring容器中

@Insert: 插入记录,并可配置主键。

循环依赖?

Bean A->Bean B->Bean A

解决循环依赖办法:设计好的层次结构,不需要循环依赖。

使用@Lazy

优选方法:二次注射;

使用Setter/Field Injection

Spring中事务?

事务的四大特性:原子性、一致性、隔离性、持续性。

在没有事务隔离时,会发生:

  1. 脏读(读取了未提交的数据,针对单笔数据)
  2. 不可重复读(进行了读取,分别读取了不同的数据,重点在于修改和删除,也是针对单笔数据)
  3. 幻读(进行了读取,分别读取了不同的数据,重点在于新增,针对多笔数据)

而Spring中事务实现分为编程式事务(硬编码)和声明式事务(包括XML和注解的配置)。

事务的5个隔离级别常量

DEFAULT(Mysql中为REPEATABLE_READ)——

READ_UNCOMMITTED

READ_COMMITTED

REPEATABLE_READ

SERIALIZABLE

(1)基于底层API的编程式事务管理:PlatformTransactionManager、TransactionDefinition 和 TransactionStatus 三个核心事务接口。

(2)基于 TransactionTemplate 的编程式事务管理(模板回调模式)

声明式事务使用到的Spring AOP机制,优于编程式事务,即有非侵入式的特点。

  1. 基于 <tx> 命名空间。dataSource、TransactionManager和代理机制三部分,无论哪种配置方式,一般变化的只是代理机制这部分;
  2. 基于 @Transactional。

数据库事务并发问题:脏读、不可重复读和幻读

不可重复读侧重于修改,幻读侧重于新增和删除操作

解决不可重复读需要锁住满足条件的行,解决幻读是锁表。

猎头——>道富

工作经历(时间):

2018年9月~2019年11月从事IT相关工作;(运维、Java后端、巡检)

2019年11月~2020年11月从事的是专利撰写工作。

上一份为何离职:转回本行做开发,是兴趣所在,后边发展方向。

间隔一年没做开发,是否能直接上岗干活?最近5个月都在补习,温故知识。

经历的一个最难处理的问题是什么?

框架或者系统环境配置不对

JDK新版本的新特性:

将ZGC(Z垃圾收集器)线程堆栈处理从安全点safepoints迁移到并发阶段;

弹性元空间能力;

启用C ++ 14语言功能。

多线程的三个特性:原子性、可见性、有序性

原子性:是指一个操作是不可中断的;

可见性:是指当一个线程修改了某一个变量的值,其他线程是否能够立即知道这个修改。

有序性:在并发时,程序的执行可能会出现乱序,会出现指令重排

Volatile

保证此变量对所有的线程的可见性,及volatile 保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新;禁止指令重排序。

什么是指令重排序:是指CPU采用了允许将多条指令不按程序规定的顺序分开发送给各相应电路单元处理。这时volatile相当于内存屏障,不能将指令重排序到内存屏障之前。

注:非volatile变量会将变量先考到CPU缓存中,各个线程有不同的CPU cache,而volatile跳过了CPU cache。

谈薪水技巧

先考虑底薪起步高点,再顾及其他福利补贴,加班工资,综合工资9+。

底薪争取要个好价钱,不光为眼前的利益,这个数字在这个岗位上更能代表你的市场价。其次关注福利奖金,及算下综合薪水,

收入=底薪+项目奖金+补贴+年终奖(一年几薪)+五险一金-考勤查扣

了解下涨薪制度,额外的项目津贴或者其他的公司内部分红。

中层管理岗位以下多是顾及公司薪资标准,不要说出薪资底线,基于你对价格环境的了解不足,可以说说刚转入新的就业环境,转退为进。

民生易贷面试

  1. Map结构存储细节

见HashMap结构

  1. 二叉树实现方式

public class BinaryTreeDemo {

   public static void main(String[] args) {

      BinaryTree binaryTree = new BinaryTree();

      HeroNode root = new HeroNode(1, "宋江");

      HeroNode node2 = new HeroNode(2, "吴用");

      HeroNode node3 = new HeroNode(3, "卢俊");

      HeroNode node4 = new HeroNode(4, "林冲");

      root.setLeft(node2);

      root.setRight(node3);

      node3.setRight(node4);

      binaryTree.setRoot(root);

      System.out.println("前序遍历:");

      binaryTree.preOrder();

      System.out.println("中序遍历:");

      binaryTree.infixOrder();

      System.out.println("后续遍历:");

      binaryTree.postOrder();

      // 前序遍历查找

      System.out.println("前序遍历查找:");

      HeroNode resNode = binaryTree.preOrderSearch(2);

      if (resNode != null) {

         System.out.printf("已找到节点,no=%d,name=%s\n", resNode.getNo(), resNode.getName());

      } else {

         System.out.printf("么有找到no=%d的英雄\n", 2);

      }

      // 中序遍历查找

      System.out.println("中序遍历查找:");

      HeroNode resNode2 = binaryTree.infixOrderSearch(2);

      if (resNode2 != null) {

         System.out.printf("已找到节点,no=%d,name=%s\n", resNode2.getNo(), resNode2.getName());

      } else {

         System.out.printf("么有找到no=%d的英雄", 2);

      }

      // 后序遍历查找

      System.out.println("后序遍历查找:");

      HeroNode resNode3 = binaryTree.postOrderSearch(2);

      if (resNode3 != null) {

         System.out.printf("已找到节点,no=%d,name=%s\n", resNode3.getNo(), resNode3.getName());

      } else {

         System.out.printf("么有找到no=%d的英雄", 2);

      }

      System.out.println("删除前,前序遍历查找:");

      binaryTree.preOrder();

      binaryTree.deleteNode(4);

      System.out.println("删除后,前序遍历查找:");

      binaryTree.preOrder();

   }

}

//创建数

class BinaryTree {

   private HeroNode root;

   public void setRoot(HeroNode root) {

      this.root = root;

   }

   // 前序遍历

   public void preOrder() {

      if (this.root != null) {

         this.root.preOrder();

      } else {

         System.out.println("二叉树为空");

      }

   }

   // 中序遍历

   public void infixOrder() {

      if (this.root != null) {

         this.root.infixOrderr();

      } else {

         System.out.println("二叉树为空");

      }

   }

   // 后序遍历

   public void postOrder() {

      if (this.root != null) {

         this.root.postOrder();

      } else {

         System.out.println("二叉树为空");

      }

   }

   // 前序遍历查找

   public HeroNode preOrderSearch(int no) {

      if (root != null) {

         return root.preOrderSearch(no);

      } else {

         return null;

      }

   }

   // 中序遍历查找

   public HeroNode infixOrderSearch(int no) {

      if (root != null) {

         return root.infixOrderSearch(no);

      } else {

         return null;

      }

   }

   // 后序遍历查找

   public HeroNode postOrderSearch(int no) {

      if (root != null) {

         return root.postOrderSearch(no);

      } else {

         return null;

      }

   }

   //删除节点

   public void deleteNode(int no) {

      //判断root是否为空

      if(root!=null) {

         //判断root是否为要删除的节点,因为之后就不会遍历到root

         if(root.getNo()==no) {

            root = null;

         }else {

            root.deleteNode(no);

         }

      }else{

         System.out.println("该二叉树为空!!");

      }

   }

}

//创建节点

class HeroNode {

   private int no;

   private String name;

   private HeroNode left;

   private HeroNode right;

  

   public HeroNode(int no, String name) {

      super();

      this.no = no;

      this.name = name;

   }

   public int getNo() {

      return no;

   }

   public void setNo(int no) {

      this.no = no;

   }

   public String getName() {

      return name;

   }

   public void setName(String name) {

      this.name = name;

   }

   public HeroNode getLeft() {

      return left;

   }

   public void setLeft(HeroNode left) {

      this.left = left;

   }

   public HeroNode getRight() {

      return right;

   }

   public void setRight(HeroNode right) {

      this.right = right;

   }

   @Override

   public String toString() {

      return "HeroNode [no=" + no + ", name=" + name + "]";

   }

   // 前序遍历

   public void preOrder() {

      System.out.println(this);// 先输出父节点

      if (this.left != null) {

         this.left.preOrder();

      }

      if (this.right != null) {

         this.right.preOrder();

      }

   }

   // 中序遍历

   public void infixOrderr() {

      if (this.left != null) {

         this.left.infixOrderr();

      }

      System.out.println(this);// 输出父节点

      if (this.right != null) {

         this.right.infixOrderr();

      }

   }

   // 后序遍历

   public void postOrder() {

      if (this.left != null) {

         this.left.postOrder();

      }

      if (this.right != null) {

         this.right.postOrder();

      }

      System.out.println(this);

   }

   // 前序遍历的查找

   public HeroNode preOrderSearch(int no) {

      System.out.println("进入前序遍历——");

      if (this.no == no) {

         return this;

      }

      HeroNode resNode = null;

      // 判断该节点的左子节点是否为空,如果不为空,则向左进行递归查找

      if (this.left != null) {

         resNode = this.left.preOrderSearch(no);

      }

      if (resNode != null) {// 说明左子树已经找到

         return resNode;

      }

      // 向右进行前序递归查找

      if (this.right != null) {

         resNode = this.right.preOrderSearch(no);

      }

      return resNode;

   }

   // 中序遍历查找

   @SuppressWarnings("null")

   public HeroNode infixOrderSearch(int no) {

      HeroNode resNode = null;

      if (this.left != null) {// 判断当前节点的左子节点是否为空,不为空,则向左递归

         resNode = this.left.infixOrderSearch(no);

      }

      if (resNode != null) {

         return resNode;

      }

      System.out.println("进入中序查找——");

      // 比较当前节点

      if (this.no == no) {

         return this;

      }

      if (this.right != null) {

         resNode.right.infixOrderr();

      }

      return resNode;

   }

   // 后序遍历查找

   public HeroNode postOrderSearch(int no) {

      HeroNode resNode = null;

      if (this.left != null) {

         resNode = this.left.postOrderSearch(no);

      }

      if (resNode != null) {// 向左递归找到

         return resNode;

      }

      // 向右子数递归进行有序遍历查找

      if (this.right != null) {

         resNode = this.right.postOrderSearch(no);

      }

      if (resNode != null) {// 向右递归找到

         return resNode;

      }

      // 如果左右子数都没右找到,则判断当前节点

      System.out.println("进入后序查找——");

      if (this.no == no) {

         return this;

      }

      return resNode;

   }

   // 递归删除指定节点

   // 思路

   /*

    * 1. 因为我们的二叉树是单向的,所以我们是判断当前结点的子结点是否需要删除结点,而不能去判断当前这个结点是不是需要删除结点.

    * 2.如果当前结点的左子结点不为空,并且左子结点 就是要删除结点,就将this.left = null; 并且就返回(结束递归删除)

    * 3.如果当前结点的右子结点不为空,并且右子结点 就是要删除结点,就将this.right= null ;并且就返回(结束递归删除)

    * 4.如果第2和第3步没有删除结点,那么我们就需要向左子树进行递归删除 5. 如果第4步也没有删除结点,则应当向右子树进行递归删除.

    *

    */

   public void deleteNode(int no) {

      //如果当前结点的左子结点不为空,并且左子结点 就是要删除结点,就将this.left = null; 并且就返回(结束递归删除)

      if (this.left != null && this.left.no == no) {

         this.left = null;

         return;

      }

      //如果当前结点的右子结点不为空,并且右子结点 就是要删除结点,就将this.right= null ;并且就返回(结束递归删除)

      if(this.right!=null&&this.right.no==no) {

         this.right=null;

         return;

      }

      //向左子树进行递归删除

      if(this.left!=null) {

         this.left.deleteNode(no);

      }

      //向右子树进行递归删除

      if(this.right!=null) {

         this.right.deleteNode(no);

      }

   }

}

  1. SpingAOP

通知包含了需要用于多个应用对象的横切行为;连接点是程序执行过程中能够应用通知的所有点;切点定义了通知被应用的具体位置(在哪些连接点)。其中关键的概念是切点定义了哪些连接点会得到通知。

切点表达式

指示器

  1. SpringCloud见有道云笔记

民航面试:

库存管理(秒杀场景)

以下为数据模型

符合秒杀开始条件时,初始化一个redis队列,存放指定数量的商品到队列中,更改活动状态为开始中;

用户在登录的状态下购买,并发用户从队列中取商品id,如果用户已经购买,将获取的商品id重新还回队列。否则消费掉,直到队列中数据为0。

public class SecKillService {

    @Resource

    private SecKillDao secKillDao;

    @Resource

    private RedisTemplate redisTemplate;

    private void check(SecKill sk) throws Exception{

       if (sk == null) {

            throw new Exception("秒杀活动不存在");

        }

        if (sk.getStatus() == 0) {

            throw new Exception("秒杀活动还未开始");

        } else if (sk.getStatus() == 2) {

            throw new Exception("秒杀活动已经结束");

        }

}

public void handle(Long id, Integer userid) throws Exception {

        SecKill sk = secKillDao.findById(id);

        check(sk);

        Integer productId = (Integer) redisTemplate.opsForList().leftPop(Constant.SEK_KILL_QUEUE + sk.getId());

 if (productId != null) {

         boolean isExisted = redisTemplate.opsForSet().isMember(Constant.SEK_KILL_BUY + sk.getId(), userid);

if (!isExisted)

{ redisTemplate.opsForSet().add(Constant.SEK_KILL_BUY + sk.getId(), userid);

            }else{

                redisTemplate.opsForList().rightPush("seckill:items:" + sk.getId(), sk.getProductId());

                throw new Exception("您已经参加过此活动");

            }

        } else {

            throw new Exception("该商品已被抢光!");

        }

    }

新闻图文存放在数据库的哪里?

首先图会以地址的形式分布在文章中,然后新闻以文本形式存在varchar或者text中,而文件可以采用本地磁盘存放。

富文本编辑器

银河商务,笔试题目:

Spring、springMVC以及SpringBoot区别

SpringBoot相对spring具有快速和简化的特点,提供Spring运行的默认配置,为Spring项目提供了很多非功能性特性。

Spring解决循环依赖问题: 在ABA情景中,容器需要注入A时,就会扫描到A正在被创建,然后直接把A的半成品注入,从而解决循环依赖的问题。

SpringMVC注解

@RequestMapping()

@Controller

@Service

@Dao

@Qualifier

@Resource

SpringBoot的优缺点

SpringBoot可以jar包的形式独立运行,嵌入了Tomcat\Jetty,能让项目快速运行,尽可能自动配置Spring。采用更多的Java Config的配置方式。

缺点:依赖比较多,缺少服务的注册和发现,缺少监控集成方案。

Struts和SpringmMVC的区别

机制:Struts的入口是filter,而SpringmMVC是servlet

Struts基于类的设计,SpringMVC基于方法

Springmvc在简洁程度和效率上优于Struts

***Callable和Runnable区别

Callable填补了Runnable在线程终止时无法返回结果的问题,Callable可以返回装载有计算结果的Future对象。

SQL优化:

5个尽可能+1个子转关联

  1. 建立索引
  2. 列顺序与复合索引的列顺序一致
  3. 尽量不要用select*
  4. 减少子查询的层数
  5. 在子查询中进行数据筛选
  6. (关联)子查询——>关联查询

注:复合索引的最左匹配原则

索引

MySQL的索引主要两种数据结构:Hash索引和B+Tree索引,是指按用户任意指定的字段对数据进行排序的一种数据结构。

添加普通索引:

ALTER TABLE `table_name` ADD INDEX index_name ( `column` ) 

Alter table member add  index tel<tel>;

添加唯一索引:

Alter table member add unique<email>

全文索引

Alter table member add fulltext intro(intro);

InnoDB引擎

SHOW VARIABLES LIKE 'storage_engine';--查询数据库引擎

InnoDB是事务型数据库的首选引擎,

引擎是决定数据以何种方式存储在硬盘里

MySQL为什么要用B+树作为数据结构?

哈希索引只能操作:in、=,不支持范围查询,eg:>,<,!=

红黑树插入数据后,数据结构会发生变化。

B+树比B树更优的地方在于,B+树的数据均保存在叶子节点中这样的好处在于:

一页能保存更多的索引

主键索引和联合索引的底层实现?

联合索引时会遵循最左前缀匹配的原则

从底层理解最左前缀原则?

在MySQL建立联合索引时会遵守最左前缀匹配原则

  • 如果有一个2列的索引(col1,col2),则已经对(col1)、(col1,col2)上建立了索引;
  • 如果有一个3列索引(col1,col2,col3),则已经对(col1)、(col1,col2)、(col1,col2,col3)上建立了索引;

联合索引类似电话簿的先姓后名的排列方式,如果只知道名,不知道姓,电话簿将没有用处。

索引失效问题?

以下语句执行时索引失效:

  1. 索引字段进行判空查询时;
  2.  对索引字段进行like查询时;
  3. 判断索引列是否不等于某个值时;
  4. 对索引列进行运算;
  5.  复合索引中的前导列没有被作为查询条件。

升序索引和降序索引?

现在只有Innodb存储引擎支持降序索引。

Asc,索引字段升序排序

Desc,索引字段降序排序

索引优化实战?

show Global STATUS,可以列出MySQL服务器运行各种状态值。

如show Global STATUS like’Innodb-page-size’;(默认是16kb)

‘a’占用多大的存储空间要看采用的字符集是什么?如UTF-8,为3个字节。

酷播面试

集合有几种?

Java集合框架主要包括两种类型的容器,一种是集合(Collection),另一种是图(Map)。Collection接口又有3种子类型,List、Set和Queue。在下面是抽象类:ArrayList、LinkedList、HashSet、LinkedHashSet、HashMap、LinkedHashMap等,Map常用的有HashMap,LinkedHashMap等。

字符流和字节流的转换?

InputStreamReader是将字节流转换成字符流,而OutputStreamWriter则相反,是将字符流转换成字节流。

信雅达

JVM、jre和jdk分别指什么?

JVMjava虚拟机,能够将 class 文件中的字节码指令进行识别并调用操作系统向上的 API 完成动作。

JreJava运行环境,包含两个部分,jvm 的标准实现和 Java 的一些基本类库。

JdkJava开发工具包,jdk 是整个 Java 开发的核心,它集成了 jre 和一些好用的小工具。

重载和重写的区别?

重载Overload:编译多态

1.重载Overload是一个类中多态性的一种表现

2.重载要求同名方法的参数列表不同(参数类型,参数个数甚至是参数顺序)

3.重载的时候,返回值类型可以相同也可以不相同。无法以返回型别作为重载函数的区分标准。

重写Override:运行多态

可以理解为在子类中把父类本身有的方法重新写一遍。在方法名,参数列表,返回类型(除过子类中方法的返回值是父类中方法 返回值的子类时)都相同的情况下, 对方法体进行修改或重写。注意子类函数的访问修饰权限不能少于父类的。

注:编译多态,编译时期只检查参数,不检查方法内部过程。

传值和传引用

以参数形式传递简单类型变量时,实际上是将参数的值作为一个副本传进方法函数的。

Java对于引用形式传递对象类型的变量时,实际上是将引用作为一个副本传进方法函数的。函数里面的引用副本所指向的是对象的地址。

数组传值的本质是传地址值的副本。

浙大网新博创面试题:

说说最近的项目?

完整的描述一个主要功能的实现?

单点登录

在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

实现SSO,所有应用系统共用一个身份认证系统;所有应用系统能够识别和提取ticket信息

实现方式:1、父域Cookie,父域中的 Cookie 被子域所共享,及子域会自动继承父域中的Cookie,通过将Session ID(或 Token)保存到父域中来实现。

  1. 认证中心——专门负责处理登录请求的独立的 Web 服务,认证中心记录用户的登录状态,并将 Token 写入 Cookie。开源项目ApereoCAS。

Jsp内置对象

9个,分别为request、response、session、application、out、pageContext、config、page和exception

Spring如何实现类和类的依赖

依赖注入

Setter注入

构造注入

使用P命名空间进行注入

Mybatis中的#和$

$将传入的数据直接显示生成在sql中,$方式一般用于传入数据库对象,例如传入表名。

#将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号,#方式能够很大程度防止sql注入,$方式无法防止Sql注入。

Mybatis在处理${}时,就是把${}直接替换成变量的值。而Mybatis在处理#{}时,会对sql语句进行预处理,将sql中的#{}替换为?号,调用reparedStatement的set方法来赋值。

SQL注入问题?

预防措施:检查数据类型和格式;过滤特殊字符;绑定变量,使用预编译语句。预编译是JDBC中的PreparedStatement类实现的。

Sql查询学生最好成绩(三张表)

MAX(score)

接口可继承多个接口吗?

可以

转发和重定向

CAS和Synchonized

CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B),如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值 。否则,处理器不做任何操作。

Synchonized见下边

SpringCloud的组成

服务的注册与发现Eureka(注册中心)、Feign、断路器(Hystrix)、路由网关(zuul)、高可用的分布式配置中心(Spring Cloud Config)、消息总线(Spring Cloud Bus)、服务链路追踪(Spring Cloud Sleuth)、断路器监控(Hystrix Dashboard)

ModelAndView

使用ModelAndView类用来存储处理完后的结果数据,以及显示该数据的视图。

麦拓科技

问项目?

如何实现高并发?

垂直扩展:增强单机硬件性能(最快的方法)

提升单机架构性能

水平扩展:只要增加服务器数量,就能线性扩充系统性能

互联网分层架构:

如何解决秒杀问题?

见民航面试题

乐观锁和悲观锁?

Lock和Synchonized区别?

Lock 的一个实现类ReentrantLock ,它拥有与 synchronized 相同的并发性和内存语义,但是添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。在激烈争用下有更佳的性能。

ReentrantLock 的特点是,拥有锁的某个线程再次得到锁,计数器+1,然后锁需要释放两次才能获得真正释放。

Lock  synchronized 明显区别: lock 必须在 finally 块中释放,否则,若受保护的代码抛出异常,锁就永远也得不到释放。

而使用同步,JVM 将确保锁会获得自动释放。

synchronized保护代码块

  1. synchronized (lockObject) {
  2.   // update object state
  3. }

  ReentrantLock 保护代码块

  1. Lock lock = new ReentrantLock();
  2. lock.lock();
  3. try {
  4.   // update object state
  5. }
  6. finally {
  7.   lock.unlock();
  8. }

当许多线程都在争用同一个锁时,使用 ReentrantLock 的总体开支通常要比 synchronized 少得多。

synchronized 和ReentrantLock 可伸缩性比较:表现都相当差,而 Lock 版本在调度的开支上花的时间相当少,从而为更高的吞吐率留下空间,实现了更有效的 CPU 利用。

同步已经成为可伸缩性的瓶颈,否则还是应当继续使用 synchronized。

synchronized 可用于JVM 的所有版本中,而Lock 不行。

公平锁和不公平锁:公平锁保证健壮性的同时需要很大的性能成本。

Java关键字

访问修饰符:public、private、protected…

修饰方法、类、变量:static、final、this、native…

数据类型关键字:int、short、long…

SQL语句,在User表中查询name值出现相同次数超过100的name和对应出现次数?

计数count(column_name)

select name,count(*) from emp group by name having count(*)>100答案未确认

金角科技

CAS和Synchonied的区别?

圆通

ConcurrentHashmap

Sychronized和lock区别

HashMap

二级索引

网达软件

运维的时候sql操作的目的是什么?

工作项目

上海诺祺科技(杭州)

开发思路及用到的技术

数据量大的如何在前端显示?

分页实现,不同数据库不同实现方式

如何获取第10~20条记录?

事务写在哪一层?

杭州广域软件

CSS

Jquery操作对象

多线程

和仁科技

AngularJS等其他前端

ArrayList和LinkedList区别?

ArrayList以数组的方式来实现List接口,特性是可以使用索引的方式来快速定位对象的位置,快速的随机取得对象的需求,使用ArrayList实现执行效率上会比较好。而linkedlist在进行insert和remove动作时在效率上要比Arraylist好。

并行和并发的区别

同太

问项目,表结构,下单场景

有公司内部的框架

Redis存的什么数据?

注入方式有几种?

中控(化工)

Hashmap结构

String、StringBuffer和StringBuilder区别

Spring中bean线程安全吗

项目运用的技术

公司项目用的SpringCloud.SAS转型

榕基

过滤器和拦截器区别?

数据库事务

有赞

Spring理解程度

迷宫算法题的讲解

杭州清柳(有面上的概率,条件相对差点)

Spring设计思想

实例化对象的方式

Http如何转成Https?

mysql写存储过程

存储过程定义:一组为了完成特定功能的SQL 语句集,存储在数据库中,经过第一次编译后再次调用不需要再次编译,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。

发现业务逻辑有问题,更改程序代码是很危险的事情,用“牵一发而动全身”来形容也不为过,而且由于本地测试代码和发布到正式环境的代码有诸多不同,因此临时更改代码非常危险。

如果我们将业务逻辑写在了存储过程里面,我们甚至无需要重新更改代码,重新编译,重新发布。我们直接更改数据库里面的存储过程就可以完成程序的相应更改,这大大方便了我们发布以后对程序的更改,增强了程序安全性。

存储过程语法

声明语句结束符

DELIMITER $$

DELIMITER //

声明存储过程

CREATE PROCEDURE demo_in_parameter(IN p_in int)

存储过程开始和结束符号

BEGIN……END

创建mysql存储过程、存储函数

create procedure 存储过程名(参数)

存储过程体

create function 存储函数名(参数)

例子:

删除给定球员参加的所有比赛:

mysql> delimiter $$  #将语句的结束符号从分号;临时改为两个$$(可以是自定义) mysql> CREATE PROCEDURE delete_matches(IN p_playerno INTEGER) -> BEGIN ->   DELETE FROM MATCHES -> WHERE playerno = p_playerno; -> END$$ Query OK, 0 rows affected (0.01 sec) mysql> delimiter;  #将语句的结束符号恢复为分号

调用存储过程

call sp_name[(传参)];

mysql触发器trigger

上海立信汇文

按照男女统计数量,写sql

Mybatis中方法名称如何调用sql?

Collection和collections区别?

Collection是集合类的上层接口,是一个Interface,里面包含了一些集合的基本操作。Collections是一个集合框架的帮助类,里面包含一些对集合的排序,搜索以及序列化的操作。最根本的是Collections是一个类。

Collections 是一个包装类,Collection 表示一组对象,这些对象也称为 collection 的元素。一些 collection 允许有重复的元素,而另一些则不允许,一些 collection 是有序的,而另一些则是无序的。

深圳问鼎资讯

请简述Struts和SpringMVC的区别

①拦截机制不同:Struts2是类级别的拦截,每次请求就会创建一个Action,和Spring整合时Struts2的ActionBean注入作用域是原型模式prototype,然后通过setter,getter把request数据注入到属性。SpringMVC是方法级别的拦截,一个方法对应一个Request上下文,所以方法是独立的,独享request,response数据,而每个方法同时又与一个url对应,参数的传递是直接注入到方法中的,是方法所独有的。

②底层框架的不同:Struts2采用Filter(StrutsPrepareAndExecuteFilter)实现,SpringMVC(DispatcherServlet)则采用Servlet实现。Filter在容器启动之后即初始化;服务停止以后坠毁,晚于Servlet。Servlet在是在调用时初始化,先于Filter调用,服务停止后销毁。

数据库部分:

在学生某一科成绩表A 中,Score字段表示成绩,className表示班级,stuID表示学生编号,name表示学生姓名,请用一个sql语句统计出平均成绩在80分以上的班级名称。

   Select className from A where avg(score)>80 group by className;

一个企业管理系统中,由于前期在用户注册模块未做重复工号(工号唯一)验证,现要求删除重复的工号数据,只保留第一次注册的工号(workID),用户表userInfo中ID为主键自增型的 int类型。请用一个sql语句写出。

Delete * from tableName where ID != min(select ID from table where count (workID)>1 group by work ID);

泛型

反射

序列化和反序列化

explain的用法

数据表设置当前时间为默认值

updatetime timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT-TIMESTAMP

JAVA中给集合赋值

Set<String>params=new HashSet<String>();

Params.add(“param one”);

Prams.add(“param two”);

Set<String>params=new HashSet<String>(){

{

add(“param one”);

add(“param two”);

}

}

Map map=new HashMap<String,String>(){{

   Put(“key1”,”value1”);

   Put(“key2”,”value2”);

}};

Int占多少字节?

(win32)44字节,在DOS中占2个字节。

循环链表:表中最后一个结点的指针域指向头节点,整个链表形成一个环,判空语句为

Head=head—>next;

Rear==rear—>next;

枚举的使用:

Public enum Color{

  RED(“红色”,1),GREEN(“绿色”,2),BLANK(“白色”,s),YELLO(“黄色”);

  Private String name;

  Private int index;

  Private color (String name,int index){

     This.name=name;

     This.index=index;

}

}

Public String toString(){

   Return this.index+”-”+this.name;

}

抽象类和接口的比较

抽象类是用来捕捉子类的通用特性的。它不能被实例化,只能作为超类使用。

接口是抽象方法的集合,如果类实现了接口,它就继承了这个接口的抽象方法,需要实现其方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值