newer所学知识点-整和
面向对象
三个特性:封装、继承、多态
理念:万物皆对象
类:创建虚拟世界的对象,属于对象的工厂
对象:构建虚拟世界的东西统称为对象
封装
变量、属性、方法、对象、类等操作都属于封装
继承
单向性:子类能访问父类的非私有属性,而父类不能直接访问子类
单一性:子类只能继承一个父类,但父类能有多个子类
传递性:继承关系可以向下或向上一直延续、传递
多态
静态多态:
重载:(当一个方法需要满足多种条件时使用)
方法名相同,参数列表不同(顺序、类型、个数)
动态多态:
重写:1.必须有继承关系 2.方法体完全相同
引用转型:父类的声明,指向子类的实例
访问修饰符
public:公共的,同项目中可访问
private:私有的,只有本类可以访问
protected:受保护的,同包,或有继承关系可以访问
friendly:缺省,同包中可以访问
static
静态的
1.程序加载时自动为其开辟内存空间,可直接通过类名进行访问。
2.静态变量存放于静态区中,所有该类的对象共享一个静态变量。
final、finally、finalize
final:
修饰类时:该类不能被继承
修饰方法时:该类不能被重写
修饰属性时:该属性诶常量,不能被修改
finally:
属于异常处理时,无论是否产生异常都会执行的代码区
finalize:
Object类的一个方法,垃圾收集器执行时会调用回收对象的该方法。可以重写该方法,进行其他相关资源的回收与释放。
抽象类、接口
相同点:
1.概念上都是抽象的,没有足够的描述信息来描述为一个对象,之中的抽象方法没有方法体。
2.需要获得抽象类、接口的实例时,需通过引用转型的方式(父类引用指向之类实例)来获得实例。
3.如果子类、实现类没有实现父类、接口的所有抽象方法,则它们也必须为抽象类或接口。
不同点:
抽象类:
1.abstract修饰
2.属性为普通属性,方法可以不为abstract修饰(既可以做声明、也可以做实现)
3.子类只能继承一个抽象类
4.从设计理念上理解:抽象类中的抽象方法为某对象的主要性质
接口:
1.interface修饰
2.属性必须为public static finally修饰,方法必须为public abstract修饰(只能做声明,不能做实现)
3.子类可以实现多个接口
4.从设计理念上理解:接口中的抽象方法为某个对象的扩展功能
包装类与八大基本类型
包装类型 优势:
可以通过方法的调用来进行各种类型的装换。而八大基本类型不可以直接转换
类型 包装类 基本类型
--------------------------------------------------
整型: Integer int
长整型: Long long
短整型: Short short
双精度浮点: Double double
单精度浮点: Float float
字符型: Character char
布尔型: Boolean boolean
字节型: Byte byte
String、StringBuffer、StringBuilder
相同点:
都属于字符串容器
不同点:
String:
属于副本类数据,无法直接改变值
StringBuffer:
值存放于缓冲区,可以对其进行修改
在多线程属于高安全、高效率
StringBuilder:
与StringBuffer类似,但是是在单线程中。
List、Set、Map区别
相同点:
1.都属于集合接口,可以存放不同类型的多个数据
2.指定泛型后,可以直接调用该类型的方法
不同点:
父接口不同:
List、Set属于Connection父集合接口,Map为父集合接口
实现类不同:
List:
ArrayList
底层结构为数组,查询快、增删慢,线程不安全
Vector
底层结构为数组,查询快、增删慢,线程安全
LinkedList
底层结构为链表,查询慢、增删快,线程不安全
Set:
HashSet、HashLinkedSet、TreeSet、
Map:
HashMap、LinkedHashMap、HashTable、TreeMap
结构不同:
List属于有序存储,值可重复
Set属于散列数据结构,无序存储,值不可重复
Map属于散列数据结构,无序存储,键值对,键不可重复,值可重复
Connection与Connections
Connection:
属于集合接口
Connections:
集合的工具、帮助类,提供一系列的静态方法,实现对集合的排序、搜索、线程安全化等操作。
常见的异常类别
ArrayIndexOutOfBoundsException(数组下标越界异常)
ClassCastException(类转换异常)
InputMismatchException(输入不匹配异常)
NumberFormatException(数字格式化异常)
ArithmeticException(数学计算异常)
NullPointerException(空指针异常)
IllegalArgumentException(非法数据异常)
FileNotFoundException(文件未找到异常)
….
异常与错误
相同点:
1.都属于Throwable类的子类
不同点:
Exception:
1.是应用程序中可预测、可恢复的问题,开发者可以进行相应处理
2.表示轻度、中度的问题
3.分为检查异常(RunTimeException)、非检查异常
Error:
1.表示程序中遇到的较严重的错误,一般开发者是无法处理的。
2.大多数错误与开发者的操作没有直接关系
字节流、字符流
字节流:
1.祖先:InputStream、OutputStream
2.不会操作缓存区(内存),直接操作文件本身。
3.以字节来进行数据的传输,传输图片、文件等非文字类型的时候使用字节流
字符流:
1.祖先:Reader、Writer
2.通过缓存区来操作文件,如果没有关闭字符流,则不会对文件本身改变。
可使用flush()来清空、输出缓冲区内容。
3.以字符的方式来传输文件,一般适用于传输文字类型。
线程的生命周期
1.创建:新创建一个线程对象
new Thread();
2.就绪:线程具备运行条件,逻辑上可运行,在等待处理机
3.运行:线程占用处理机,正在运行
4.阻塞:放弃了CPU使用权,在等待一个事件,逻辑上不可运行
同步阻塞:获取同步锁,如果锁被占用,放入锁池中
等待阻塞:O.wait(),JVM将该线程放入等待队列
其他阻塞:sleep()、join()、I\O处理等
5.死亡:执行结束或异常退出
Thread与Runnable
相同点:
都属于线程
不同点:
Thread:
1.通过继承实现,有单继承的限制
Runnable:
1.接口,通过implements实现
2.可实现资源的共享
Synchronized同步
1.多线程编程中提供上锁的功能。当代码块、方法添加了同步后表示该区域只能同时有一个线程执行,其他线程进入等待队列。
2.同步上锁的是对象,而不是代码,所以不同对象的锁是互不干扰的。
泛型
1.字面上理解:广泛的类型。可以存放任意的数据类型
2.“参数化类型”,将原来具体的类型参数化
3.使用上分为:接口、类、方法泛型
4.逻辑上可以看成是多个不同的类型,实际上都是同一个类型
5.类型通配符:“?”,也有类型通配符上限(? extends ...)与下限(? super …)
反射简述
概念:
1.在程序运行时,对于类:可以获得的任何的方法和属性。对于对象:可以调用任一个方法和属性。这种动态获取信息以及动态调用对象的功能我们称之为java的反射机制。
2.可以将类中的各种成分(构造方法、普通方法、成员变量、包),映射成一个个对象。
3.反射的原理在于class对象。(class对象的由来:将Class读入内存并为之创建一个Class对象)
sleep、wait
sleep:线程休眠,拥有自动唤醒功能,且线程队列不会改变
wait:线程暂停,必须在同步中实现,需要通过notify()等方法进行唤醒
TCP\IP
应用层:为网络排错、文件传输、远程控制和internet操作提供应用程序。
存在HTTP(超文本传输协议)、FTP(文件传输协议)等
传输层:为网络提供了流量控制、错误控制、确认服务。
TCP(传输控制协议)、UDP(用户报表协议)
网络层:实现物理地址与逻辑地址的转换
IP(互联网协议)
网络接口层:物理层次的一些接口。如“电缆”
各种物理通信的网络接口。
URL解释
解释:
Uniform Resource Locator:统一资源定位器
例:
protocol :// hostname[:port] / path / [;parameters][?query]#fragment
protocol:指定使用的传输协议。file、ftp、http…
hostname:主机名或ip地址
port:端口号
path:路劲
;parameters:参数,制定特殊参数的可选项
?query:查询,可有多个参数,使用“&”分隔
fragment:信息片段,指定网络资源中的片段
关系型数据库、非关系型数据库
关系型数据库:(SqlLite、Oracle、MySql)
1.是通过关系模型来建立数据库,通过外键关联来建立表与表之间的关系。
2.具有事务的一致性。
对象型数据库:(MongoDB、Redis、HBase)
1.数据以对象的形式存储在数据库中,而对象的关系由对象的属性来决定。
2.使用键值对来存储数据
3.分布式
4.一般不支持ACID操作
5.严格来说不是数据库,而是一种数据结构换存储方法的集合。
数据库设计的三范式
1.原子性:
将所有数据分解为不能再分解的最小单位,合并相似属性避免产生冗余数据。
2.如果数据列中出现重复数据,就要把表拆分开来。(一张表只做一件事情)
3.数据不能存在传递关系,即每个属性都与主键有直接关系而不是间接关系。
servlet生命周期
1.启动web容器后,容器通过加载器加载servlet类。
2.创建servlet实例(一般在第一次请求时创建,也可load-on-start设置启动时创建)
3.servlet调用init()方法进行初始化。(装载至内存中,生命周期中只执行一次)
4.servlet调用service()分发请求、响应对象给doGet或doPost等方法进行处理。
5.调用destroy()方法对servlet进行销毁,解除资源的占用。
JSP九大内置对象
1.ServletContext》application
可将信息保存至服务器中,直至服务器的关闭。
2.PageContext》pageContext
取得JSP页面任何范围的参数,可在jsp页面直接使用pageContext
3.Object》page
只有在当前JSP页面内才算合法,类似this。
4.ServletConfig》config
取得服务器的配置信息。通过pageContext的getServletConfig()获取该对象。
5.JspWriter》out
用于web浏览器输出信息,并管理应用服务器上的缓冲区。
6.Throwable》exception
显示异常信息,只有在设置了“isErrorPage = "true"”的页面才可以使用。
7.HttpServletResponse》response
JSP容器处理过的信息返回给客户端。作用域:本页面。
8.HttpServletRequest》request:
客户端的请求信息(头、系统、请求方式、请求参数等)。作用域:一次请求。
9.HttpSession》session
由服务器创建的关于用户请求的对象,每个用户都有一个session对象。底层采用Map集合实现,所以是Key/Valeu的格式。
session和cookie
session:
1.session对象存放于服务端,访问数量过多会影响服务器性能。
2.用户发送请求时,创建一个session并用唯一id标识。默认保存30分钟。
3.session是通过cookie来工作的
cookie:
1.数据保存在客户端,不会占用服务器资源。受浏览器设置影响。
2.不安全,可以通过获取cookie来进行cookie欺骗。
3.cookie保存的数据不能太大(cookie存放至请求头,太大会被拒绝访问)
Ajax
1.一种创建交互式网页的,网页开发技术。(Asynchronous JavaScript And XML)
2.再无需加载整个网页的情况下局部刷新网页中的数据。
同步、异步区别
1.同步:发送一个请求,必须等待结果处理并返回,才能处理下一个请求
一般运用于数据的添加、银行的转账系统等。
2.异步:发送请求时,无需等待结果的返回,随时可处理下一个请求。
GET、POST
GET:
1.后退或刷新时是无害的。
2.长度有限制,GET方法向URL中写入数据,URL长度限制为2048个字符。
3.安全性较差,因为所发的数据是url中的一部分。
4.能被缓存。
POST:
1.后退或刷新时,数据会被重复提交。
2.长度没有限制。
3.安全性比get好,数据不会保存在浏览器历史或web日志中。
4.不能被缓存。
Forward、Redirect
Forward(效率高)
1.请求转发:转发页面或转发到的页面可以共享request数据。
2.客户端的浏览器地址不会改变。
服务器直接访问目标地址,把该地址的响应内容读取出来,客户端不知道内容来自哪儿
3.一般用于登录,根据角色转发到不一样的模块
Redirect(效率低)
1.重定向:不能共享数据。
2.地址会改变(因为服务器返回一个状态码,让客户端重新去访问新的地址)
3.一般用于用户注销,跳转至其他页面。
Hibernate框架与Mybatis框架
Hibernate:
1.自动化ORM(对象关系映射)框架,持久层框架。
2.sql语句自动生成,移植性较好,但是对sql语句的优化、修改比较困难。
3.无需过多关注底层实现,只要着重管理对象。
MyBatis:
1.半自动化ORM框架,持久层框架。
2.支持定制化sql、存储过程、高级映射。SQL语句的优化、修改比较方便。
3.需要自己配置映射关系。
其他:
持久层:由于用户、商品等数据如果都存放在内存中的话只要一断电或其他事故则会造成数据的丢失,通过持久层框架可以实现数据存储在本地硬盘(如数据库)上实现数据的持久化。
MyBatis的${}与#{}区别
${}:
是直接的字符串替换,在动态解析过程中进行了替换,会造成SQL攻击注入的问题。(拿到的是具体的值)
#{}:
通过设置占位符,先预编译之后再在DBMS阶段进行字符串的替换,安全性更高一些。(拿到的是一个字符串)
json
一种轻量级的数据交换格式,可以将对象、数组等通过key/value的方式转换为字符串,也可以再将该字符串在装换为对象。
Maven
1.项目管理、综合工具。简化了项目的建设
2.使项目可重复使用、易维护、更容易理解的综合模型
MVC模式
Model:
模型层,负责处理业务数据,对数据库进行操作,提供数据给视图。
View:
视图层,用户看到并与之交互的界面。
Controller:
控制层,处理业务逻辑。接受用户请求,并调用视图、模型来完成用户需求。
优势:
分为三层,使项目的结构更加清晰、更加易于开发与维护
提高了代码的复用性
解耦合,降低了代码之间的耦合度
。
Tomcat、Jetty服务器
相同点:
都属于一种servlet引擎,都支持servlet的规范与javaEE的规范
Tomcat:
1.基于容器实现,扩展需要对tomcat的体系架构、实现过程有一定了解。
2.Tomcat对javaEE的支持更加全面。
3.用于处理频繁、短时间的连接。
Jetty:
1.基于hadler实现,更易于做扩展,对新的servlet规范支持较好。
2.可实现按需加载,减少内存的占用。更加快速、修改简单。
3.用于处理大量、长时间的连接。
Spring框架
简介:
1.spring框架是一个高度灵活的、轻量级的框架
2.是一种无侵入式的设计方式
3.开发的目的是降低企业级开发项目的复杂度
功能模块:
AOP:
面向切面编程,可理解为过滤器,降低代码的耦合度、提高可维护性。
ContainerCore:
核心容器,提供了IOC(控制反转)、DI(依赖注入),其他功能模块是建立在该模块的基础上的。
DAO:
数据操作模块,集成了MyBatis,实现了对事物的控制
WEB:
集成了SpringMVC的支持
Test:
提供单元测试功能
解释控制反转、依赖注入
控制反转:
将原本手动创建的对象的控制权,交由spring来进行管理,由spring来声明为一个bean组件
依赖注入:
在spring创建一个对象时,动态的将依赖对象注入到bean组件中。
bean生命周期与作用域
生命周期:
1定义bean组件
2.初始化bean组件。
a.调用init-method方法实现初始化
b.实现org.springframework.bean.factory.initializing接口
3.bean的调用(三种方式获得实例)
4.bean的销毁
a.调用destroy-method方法(多实例时,需程序员手动销毁)
b.实现org.springframwork.bean.factory.DisposeableBean接口
作用域:
singleton:单实例
spring中只会存在一个共享的bean实例,并且
prototype:多实例
每次对该bean请求,都会创建一个新的实例
request:请求
再一次http请求中(只在基于web的applicationContext中有效)
session:会话
一次session会话中有效,
globel session:全局会话
一个全局的session会话
事务
特点:(ACID)
原子性:要么全部成功,要么全部失败回滚
一致性:事务完成时,必须是所有数据都保持一致
隔离性:多个用户并发操作数据库时,不能相互影响
持久性:一旦事务完成,对数据的改变就是永久的。
脏读、幻读、不可重复读
脏读:
会读取数据中未提交的数据。
不可重复读:
多次读取数据时,数据的结果不一致
幻读:
针对一阵批数据,前后读取的数据不一致。
SpringBoot
1.是基于spring开发的一个集成框架。
2.内嵌了Tomcat、Jetty等web容器。
3.之中提供了众多starter简化了Maven的配置,避免出现大量的配置信息。
RestFul(表象性状态转变)
1.一种架构风格、设计风格,是一种思想
2.减少没必要的请求描述。get:查询、post:添加、put:修改、delete:删除。
3.根据不同的结果设置返回不同的状态码。
200(成功)、204(操作成功,但是没有返回数据)、400(请求参数有误)、404(资源未找到)、500(内部服务器错误)
跨域
1.域名、协议、端口不同则构成跨域(如:当使用前后端分离)
2.跨域是不允许被直接访问的。
Spring注解方式解决:@CrossOrigin
Spring通过配置类解决:UrlBasedCorsConfigurationSource、CorsConfigruation
ajax解决:xhrField{withCredentials:true}
JVM垃圾回收机制(Garbage Collection)
收集并删除未引用的对象,通过System.GC()来触发垃圾回收机制
1.区域:(如何才会触发gc)
年轻代:(Eden、Survivor1、Survivor2)
当年轻代空间满时触发minorGC。采用复制算法,回收时将Eden中还存活的对象一次性复制到另外一块survivor,然后清理掉Eden和刚才用过survivor空间。没进行一次Minor GC(年轻代回收),对象的年龄就增加1岁,年龄到一定程度(默认15岁),就会被移到老年代。
年老代:(Tenured)
如果年老代的剩余空间不足时,触发年老代的回收机制full GC
非堆内存:(Permanent)(永久代、方法区)
类的对象实例全被GC、同时类加载器也被GC就会触发方法区中对象的GC
2.判断需要回收的内存
引用计数法:回收引用计数器为空的对象(jvm不使用这种方法)
可达性分析:通过gcroot开始搜索,搜索不到的对象
3.回收什么
回收new出来的对象,对于不是new出来的对象需要通过finalize()方法来进行回收
4.三种基本GC算法
标记/清除算法
内存耗尽触发,1.开始标记可达对象2.清除root不可达对象
复制算法
1.标记可达对象2.将可达对象复制到空闲区3.清空原来的区域
标记/整理算法
1.标记2.整理(执行压缩、整理到一个连续的空间)
笔试题汇总
如何获取下个星期三是哪年哪月哪日
1.通过getDay方法获取当前日期与下个星期三相差几天,然后获取当前日期的时间戳加上相隔天数的时间戳。再通过日期格式化对象(SimpleDateFormate)转换格式
sdf.format(new Date(new Date().getTime()+1000*60*60*24*(7-new Date().getDay())));
2.通过Calendar对象,获取当前日期的星期与下个星期三的相差天数(7-当前星期+3)然后通过add()方法增加获取的天数,之后格式化输出
Calander cla = Clander.getInstance(); //获取日历对象
cla.add(Clander.Date,7-Clander.WEEK_OF_MONTH+3); //添加天数
之后格式化输出。
进程和线程的区别
进程:
资源分配的最小单位(相当系统的一个应用程序)
有独立的堆栈空间与数据段,一个进程的崩溃不会影响其他进程
线程:
程序执行的最小单位(线程是进程的轻量级实现)
有独立的堆栈空间但是共享数据段(共享地址空间),一个线程的死亡会导致进程程的死亡
java的加载机制
虚拟机将描述类的数据从class文件中加载至内存,并进行校验、转换解析、初始化,最终形成能够被虚拟机直接使用的java类型。
实例变量(成员变量)与静态变量(类变量)
加载不同:
实例变量随着类的实例的创建而存放与堆中
静态变量在类加载时就将其存放在了方法区中(方法区中的静态区)
声明周期不同:
实例变量随着对象的消失而消失
静态变量随着类的消失而消失
String s = new String("abc");实例化了几个对象
两个:(String s:这个是引用,不算对象)
第一个存放于堆区:new String();
第二个存放在字符串常量区:"abc";
对象创建的几种方式
1.new 一个对象:
Test t = new Test();
2.通过Class的newInstance():(需要进行强制转换)
Test t=(Test)Class.forName("xxx").newInstance();
3.通过对象的clone()方法(需要实现Cloneable接口):
对象.clone();
4.通过对象流方法(需要实现Serializable接口)
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("xxx"));
oos.writeObject(t); //向文件中写入对象
ObjectInputStream ois= new ObjectInputStream(new FileInputStream("xxx"));
Test t = (Test)ois.readObject(); //读取出对象
行转列
先按一个类(比如名字)来进行分组(GROUP BY),然后给需要的字段去个别名(如:语文、数学),之后通过判断显示相关的数据(如:if(course='语文',score,0)别名)。实现行数据变为一列。
MyBatis缓存
一级缓存:
1.SqlSession缓存,基于HashMap的本地缓存,不同SqlSession的缓存数据区域不会互相影响
2.第一次查询会缓存,第二次查询之后就不会再查询数据库,而是直接从缓存获取。每次增、删、改都会清空缓存,保证缓存中的信息是新的。
二级缓存:
1.Mapper的缓存,基于HashMap,多个SqlSession共享二级缓存。
2.作用域是mapper的同一个namespace。不同SqlSession执行相同的namespace下的SQL语句,第二次查询的会使用第一次查询的缓存。
3.默认没有开启,开启需要在配置文件中写入:
mapper的xml中配置:(顺序排在第一位)
获取properties文件
删除重复列
DELETE from item_process WHERE OPT_TIME NOT in(select OPT_TIME from(select OPT_TIME FROM item_process group BY item_status_desc)a);
1.Mysql需要在分组(GROUP BY)的结果集外层在套上一个查询(select)。因为MySql不能先查询出同一表中的某些值,在更新(update、delete)它。
2.将重复字段合并,取最大值(需要是一个唯一的值),然后删除除了查询出来的所有记录(not in)
如何创建索引
分为:
普通索引:
ALTER TABLE 'table_name' ADD INDEX 'index_name'('column');
唯一索引:
ALTER TABLE 'table_name' ADD UNIQUE 'index_name'('column');
主键索引:
ALTER TABLE 'table_name' ADD PRIMARY KEY 'index_name'('column')
全文索引:
ADD FULLTEXT
组合索引:
('column1','column2')
作用:
1.数据库可以根据索引直接定义到该索引位置,大大减少了遍历匹配行数。
2.但是如果在需要查询所有数据的情况下,索引反而会加大数据库的负担。
3.每次增、删、改都会造成索引的重新计算更新,开销太大
其他:
1.可以在查询语句前加上“EXPLAIN”来查看查询情况
2.使用“CREATE INDEX”方式创建索引(只能有两种)
CREATE INDEX index_name ON table_name(column_name);
CRETE UNIQUE INDEX index_name ON table_name(column_name)
集合的线程安全问题
1.使用synchronized关键字,在方法或代码块添加,实现多个线程访问时的线程安全。
2.使用Connections类提供的SynchronizedList()方法创建一个安全线程。
JDBC调用存储过程和函数
作用:
封装SQL语句,提高SQL语句的复用性
存储过程:可以有多个返回值
创建: CREATE PROCEDURE test(in a int)
BEGIN INSERT INTO test VALUES(a);
END
JDBC调用:
获取存储过程:CallableStatement cs = conn.prepareCall("{Call 存储名字(?,?)}")
注册返回值:
函数只能有一个返回值
栈与队列的区别
相同点:
1.都是线性结构。
2.插入操作都是限定在表尾进行
3.增、删的时间复杂度都为O(1),空间复杂度两者也一样。
4.多链栈和多链队列的管理模式可以一样。
不同点:
1.删除数据元素的位置不同:
栈在表尾进行,队列在表头进行
2.常见应用场景不同
栈:括号问题的求解、表达式的转换和求值、函数调用和递归实现、深度优先搜索遍历等。
队列:计算机中各种资源的管理、消息缓冲器的管理、广度优先搜索遍历等。
3.顺序栈能够实现多栈空间共享、顺序队列不行
创建一个单实例
IOC创建对象的过程
简述一下版本管理工具
需要了解的知识
数据链表
定义
在物理内存上不连续的数据结构
常用的设计模式
优化
前端优化:
浏览器访问优化:
减少Http请求数量(合并css、JavaScript、图片)
使用浏览器缓存:设置HTTP响应头中的Cache-Control和Expires属性
CSS前置、JavaScript后置
减少Cookie传输
CDN加速:
本质是缓存、将数据缓存在离用户最近的地方
反向代理:
缓存了用户访问的热点资源,可以直接从反向代理将某些内容返回给用户浏览器
服务器优化:
1.分布式缓存:本质就是内存中的哈希表
2.异步操作:异步将高并发产生的时间消息存储在消息队列中
3.代码优化:多线程优化、对资源进行并发访问时,采用锁机制,非阻塞I/O
数据库优化:
redis有哪些好处
1.速度快,数据存在内存中,类似于HashMap。(基于内存的高性能key/value数据库)
2.支持丰富数据类型,支持String、list、set、等
3.支持事务、操作都是原子性
4.可用于缓存、消息
富文本编辑
PDF导入导出
网络爬虫