本文章主要用于本人学习java时的知识总结,争取每周一篇。若有大佬路过,恳请各位斧正,不胜感激。
计算机基本常识
什么是软件?
从物理组成的角度讲,软件是一系列指令和数据的集合。控制计算机完成一系列特定任务,计算机系统一切的硬件必须有软件才能真正工作,实现硬件的价值。
软件应该包括:
数据
程序
文档
现代软件组成
1.程序代码:源代码
编译/解释得到的可执行文件
2.数据:输出数据
输入数据
配置文件
3.库和框架:库:提供特定功能代码的集合
框架:提供开发基础结构和工具
4.运行时环境:虚拟机/解释器
依赖管理
5.用户界面: 命令行界面 --CLI
图形界面 -- GUI
6.数据库
7.网络通信:API:用于不同软件和模块之间的通信
协议:如HTTP、WebSocket等
8.文档:用户文档
开发者文档
9.测试和调试工具
10.部署和运维工具
目前软件的典型架构
单体架构:
所用功能模块集中在一个应用程序中,适合小型项目
微服务架构:
将应用程序拆分为多个独立服务,每个服务负责特定功能,适合大型复杂系统
事件驱动架构:
基于事件触发,适合实时数据处理和异步任务
云原生架构:
基于云计算平台,一般包括:容器 微服务 DevOps工具
两类软件的具体分组成
一个电子游戏:
游戏程序文件:
可执行文件,如exe等
动态链接库
游戏资源文件:
图形、音频、视频、配置文件等
游戏引擎和运行时环境:
游戏引擎:游戏的核心框架,负责渲染、物理模拟、音频处理等
如Unity、Unreal Engine
用户界面文件
脚本和数据库
文档和帮助助手
更新和补丁文件
存档和日志文件
其他文件
一个购物网站 ---Web程序:
Web程序:一种基于浏览器(客户端)与服务器之间交互的应用程序
Web程序可以在任何支持web浏览器的设备运行,无需独立安装
通常基于HTTP/HTTPS协议进行通信
通常采用前端+后端+数据库架构
可以认为,一切动态网站都是web程序
Web程序的组成:
前端:
HTML:定义网页的样式和布局
CSS:控制网页的样式和布局
JavaScript脚本:实现动态交互,如按钮点击,数据的加载等
前端资源与库:静态资源:图片、视频、字体等
框架:简化开发,提供组件化和状态管理,构建动态用户界面
典型例子:React、Vue.js、Angular
浏览器缓存:缓存静态资源以提高加载速度
API调用:通过HTTP请求实现与服务器端的交互
后端:
Web服务器:接收HTTP请求并返回响应
如:Apache、Nginx、Tomcat等
应用服务器:执行业务逻辑
数据库:存储和管理数据
API接口:提供与前端交互的接口
身份验证和授权:管理用户登录和权限控制
缓存:提高数据访问速度,如Redis缓存热门商品数据
文件存储:存储用户上传的文件
消息队列:用于处理异步任务
日志和监控:记录系统运行状态和错误信息
部署和运维:管理应用的部署和运行
前后端的交互:
通信方式:通过HTTP/HTTPS协议通信
通信数据:一般使用JSON格式传输数据
知识扩展
1.浏览器的缓存
当用户打开一个网页,浏览器会将HTML文件、CSS样式表、js文件以及图片、视频、字体等资源下载到本地计算机存储设备,作为缓存,当用户下次打开相同的界面可以从本地加载而不需要再从服务器下载。
浏览器的一般缓存顺序:html文档→CSS样式表→js文件→图片等静态资源
不同的浏览器的缓存路径可能不同。
浏览器的缓存系统会根据资源的类型和大小,采取不同的缓存策略:
内存缓存:
一些短时间会频繁访问的资源(小型js文件、HTML文档CSS)会直接存储在RAM
当刷新网页时浏览器会优先从内存加载这些资源而不是去服务器下载
这部分资源当浏览器关闭时也会被清空
磁盘缓存:
绝大部分资源会存储到计算机的缓存设备(HDD/SSD)上
比如HTML、CSS、js文件、图片、视频等
即使浏览器关闭这部分资源也不会消失
下次打开网页浏览器会自动从存储设备读取这些资源
2.著名的web服务器介绍
Apache:主要用于静态资源服务,不支持Java Serevelet和JSP
Tomcat:一个 Java Serevelet容器+Web服务器,运行Java编写的应用程序,是Java EE规范的一部分。
Apache与Tomcat可以分开工作,也可以组合工作:
Apache:负责静态资源的处理
实现负载均衡:将请求分发到多个Tomcat实例。
目前,相比Apache,有一个更流行的前端服务器Nginx。
Nginx高并发性能更好,并且资源占用更低,Tomcat+Nginx是现代web应用的常见模式
但是,如果要处理多种动态内容,如python、PHP等,或需要复杂的模块支持,Apache是更好的选择。
JavaSE
1.Java程序设计概述
java不只是一种高级程序编程语言,也是一种完整的开发平台。
Java的特点
java设计白皮书总结了Java的11个关键词:
简单性、面向对象、分布式、健壮性、安全性、体系结构中立、解释性、可移植性、高性能、多线程、动态性。
简单性:
java的语法相比c/c++更简单,同时,java的体积更小
面向对象:
java遵循面向对象思想,但java中没有实现多重继承,而是用接口这个概念实现这个功能。
体系结构中立:
java的基本数据类型的大小和有关运算是固定的,不会随着操作系统等不同而发生变化
解释性与可移植性:
java程序编译与解释过程
字节码文件可以在任何实现了JVM的设备上运行,因此,java程序可以很方便的移植到不同平台。
高性能:
Java的解释器采用一种称为即时编译的策略,加强了Java的运行速度。
即时编译:将执行最频繁的字节码直接转成机器码
Java常见术语
术语 | 解释 |
---|---|
JDK | 编写java程序的软件 |
JRE(Java运行时环境) | 用来运行java的软件 |
SE | 用于桌面应用或简单服务器应用的java平台 |
ME | 用于小型设备的Java平台 |
EE(现更名为 Jakarta EE) | Java企业级开发平台,应用于大型分布式应用 |
OpenJDK | Java SE的开源实现 |
LTS | 长期支持版本 |
Java SE 与 Java EE
Java EE是企业级应用开发平台,常用于开发大型分布式应用。
Java EE相比 Java SE实现了一些独特的API和规范:
Web组件:Servelet 、JSP、 JSF
企业服务: EJB、JMS
数据持久化:JPA、JDBC
安全性:JAAS
Web服务:JAX-RX、JAX-WS
依赖注入:CDI
Java EE 的运行依赖 Java EE服务器,如Tomcat等
但是:
Java EE 脱胎于Java SE,Java EE 没有独有的语法,Java EE的开发同样需要JDK,运行也必须有JVM,Java SE 是 Java EE 的基础。
特性 | java SE | Java EE |
---|---|---|
基本Java语法 | 支持 | 支持 |
集合框架(List、Map、Set) | 支持 | 支持 |
I/O操作(文件、网络) | 支持 | 支持 |
多线程 | 支持 | 支持 |
GUI | 支持 | 一般不用于企业开发 |
JDBC(数据库连接) | 支持 | 支持,但Java EE更推荐JPA |
JPA(持久化API) | 不支持 | 支持 |
Servelet(Web开发) | 不支持 | 支持 |
JSP(动态网页) | 不支持 | 支持 |
EJB(企业级Java Bean) | 不支持 | 支持 |
JMS(消息队列) | 不支持 | 支持 |
JAX-RS/JAX-WS(Web服务) | 不支持 | 支持 |
CDI(依赖注入) | 不支持 | 支持 |
Java SE 程序示例:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, Java SE!");
}
}
Java EE程序示例:
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<h1>Hello, Java EE!</h1>");
}
}
2.java编程环境
JDK的安装
Oralce公司提供java的最新版本,市面上也有很多的开源jdk称为 OpenJDK。
一般都有对应的安装程序,直接运行即可。路径不要有空格。
安装后,需要配置环境变量---将jdk安装路径中的bin目录添加到可执行路径中。
可执行路径:操作系统查找可执行文件时所遍历的目录。
命令行工具
现目前的集成开发环境--IDE都十分成熟了,但是使用命令行工具编写java也是一种基本技能,可以迅速的验证jdk的安装是否正确。
主要使用的工具(命令):
javac ---java源代码的编译器,注意编译时文件名需要后缀。(.java)java文件必须与公共类名相同,并且首字母大写。
java:调用JVM对字节码进行解释,得到可执行程序,注意java命令不需要添加后缀。
集成开发环境
使用IDEA。
目前学会的快捷键:
-
ctrl+D:将当行复制粘贴到下一行
-
选中字符后,shfit+F6:重构,修改整个项目中出现的选中的内容
-
ctrl+alt+shfit+J:多重选择
-
ctrl+/:将选中的内容集体注释
JShell
一种运行java程序的shell,主要的工作流程:
读取→Jshell评估输入→打印输出
Shell:
一种特定的应用程序,核心功能:
解释和执行用户输入的命令,并管理进程、文件系统等操作系统资源。
特性 | shell | 一般应用程序 |
---|---|---|
主要功能 | 解释和执行命令,管理系统资源 | 提供特定功能如编辑文本、浏览等 |
交互方式 | 命令行界面CLI | 图形界面GUI或CLI |
运行环境 | 用户空间,直接与内核交互 | 用户空间,通过API与系统交互 |
自动化能力 | 强大的脚本支持 | 有限的脚本或宏支持 |
注:并不是只有类UNIX系统才有shell。Windows系统也有,如cmd、PowerShell等
计算机网络--HTTP协议
目前主要的web应用都是基于HTTP/HTTPS协议实现通信的,可以说,没有HTTP,以及HTTP协议所属的TCP/IP协议族,就没有互联网。
1.基本概念
协议:不同的计算机系统为了实现通信,必须约定一套一起遵循的规则,这种规则就是协议。
HTTP:超文本传输协议
HTTP基本理念:借助多文档之间的相互关联形成的超文本,连成可以互相参阅的万维网 ——www
构建www的技术:
-
HTML:一种使用标准通用标记语言(SGML)作为标记的语言
-
URL:指定文档所在地
-
HTTP:文档传递协议
2.TCP/IP协议族
一切与互联网通信相关的协议的集合,就是TCP/IP协议族。
TCP/IP协议族约定的内容包括:
电缆的规格
IP地址的选定
寻找异地用户的方法
....
TCP/IP的分层管理
为了简化设计,方便管理,TCP/IP协议族从功能的角度,将互联网分为了四个层次:
应用层
传输层
网络层
数据链路层
每一层的协议专注于实现自己的功能,层与层之间,虽然会相互通信,但除此之外,相互并不会有任何影响。这样的模式大大简化了程序设计的难度。应用层的程序只需要关心应用程序设计本身,而不用去担心信息是否会准确送到等。
应用层
应用层决定了用户进行通信时应该提供怎样的通信服务。
应用层协议:HTTP、DNS、FTP。。。
传输层
传输层的协议主要用于实现数据完整、有序的被传输到目的地。
主要协议:TCP、UDP
网络层
网络层主要是为了实现路由,即:找到数据从源到达目的地的路径。
实现路由的核心:IP地址
数据链路层
这一层主要实现数据在具体的物理设备之间的传输。
实现功能的核心:MAC地址
TCP/IP的数据传输流
当一次通信进行时,用户从客户端或浏览器端发送的数据,会一层一层的依次向下传递,每一层依照分层模式设计的功能,对数据进行处理,最终成功将数据传送到目的地。
TCP/IP协议族数据流
具体来说:
1.应用层:针对用户的通信信息,生成HTTP请求。
2.传输层:(以TCP协议举例):收到HTTP请求后,为了方便传输,将大的数据划分成有序的一段段的报文段,在每个报文段上添加编号与端口号,之后将每个报文段交给网络层
3.网络层:对报文段添加目标IP、源IP等信息将报文段组成数据包,交给数据链路层。
4.数据链路层:为数据包添加上目标MAC地址等信息,调用网卡驱动,将数据转换成电信号或无线信号发送出去。
上述的过程称为数据的封装。
虽然逻辑上这些步骤是分层完成的,但其实从物理位置的角度讲,每一步都是在源计算机系统上完成的。
其中,只有应用层生成HTTP请求是通过不同的HTTP客户端应用程序实现的。
TCP协议分割报文等工作,并不是由具体的应用程序来控制的,而是由操作系统的内核中的TCP/IP协议栈来自动完成。
以liunx系统为例:
层次 | 逻辑上的行为 | 对应的实现 |
---|---|---|
应用层 | 应用程序调用socket()创建套接字,并调用send()发送数据 | net/socket.c:实现 scocket API(socket()、bind()、listen()) |
TCP层(传输层) | TCP将大数据拆分成报文段;TCP计算校验和,确保数据完整性;TCP维护重传机制,保证可靠传输 | net/ipv4/tcp.c:实现TCP逻辑(重传、控流) |
IP层(网络层) | 为TCP报文添加IP头;查找路由表,确定下一跳地址 | /net/ipv4/ip_output.c处理IP数据包的发送 |
数据链路层 | 添加MAC头;调用网卡驱动,将数据转换为电信号或无线信号发送出去 | drivers/net/:网卡驱动,发送数据到物理网络 |
前端知识学习
1.HTML
从架构的角度去看,一个网站应该包括
从网站的角度去看,一个网站应该包括:
-
骨架–HTML文档
-
修饰–CSS样式表
-
行为 –JS脚本
HTML的定义:超文本标记语言
-
超文本:文件中不止有字符,还有图片、视频、超链接等资源
-
标记:HTML中的一切都应该处于标记中
HTML语法
语法规范:
-
HTML的标签,必须提供一个结束标签,表明结束,如<p><p>
-
单标签:指没有内容且不需要结束标签的HTML标签,这些标签本身 自包含(逻辑上讲一个标签内容就能够结束)
-
对于标签属性值的引用,必须用括号包括
-
属性:为标签提供额外的信息
-
属性通常以键值对的形式出现,由属性名和属性值组成
-
例:<html lang="en">
-
-
标签必须嵌套正确
-
必须添加文档类型声明
-
作用:告诉浏览器当前网页使用的是那种版本的HTML或XHTML
-
例:
<!DOCTYPE html>
-
常见标签
<html>
-
概述:是html的根标签,表示一个网页的开始与结束,一个网页的所有标签必须包含在一对<html>内。
-
属性:lang。
-
作用:让浏览器识别本网页或指定的HTML元素的语言,通常位于<html>标签中,作为全局属性。
-
例:<html lang=“ch-ZN”>
-
常见值:
-
“en”:英文(默认)
-
“zh-CN”:简体中文
-
“zh-TW”:台湾版繁体中文
-
-
<head>
-
概述:用来定义文档的元数据(metadata),这些数据并不会出现在网页的主体部分。<head>标签包含的部分称为网页头部。
-
常见的元数据:
-
标题<title>
-
字符集<meta>
-
样式表<style>或<link>
-
<meta>
-
概述:元数据标签,用于提供网页的描述、字符集、关键字等信息。
-
常见属性:
-
charset:
-
作用:指定网页使用的字符编码
-
常见值:
-
“UTF-8”:最常见的字符编码,支持几乎所有语言
-
-
-
name:
-
作用:指定元数据的名称,为搜索引擎或浏览器提供特定信息。
-
常见值:
-
description:网页的简短描述,搜索引擎通常将它作为网页的摘要
-
keywords:网页的关键词,搜索引擎根据这些关键词优化搜索结果。(现代搜索引擎不再主要依赖这个标签)
-
author:网页作者的名称
-
viewport:定义网页在移动设备上显示方式,特别是如何控制缩放和视图宽度等。
-
-
-
http-equiv:
-
概述:将元数据与HTTP头信息相关联,通常用于模拟HTTP头的功能。
-
常见值:
-
cotent-type:指定网页的字符编码,通常与charset属性一起使用。
-
refresh:定义页面自动刷新时间或跳转
-
X-UA-Compatible:指定IE浏览器的兼容模式,通常用于确保浏览器使用最新的渲染引擎
-
-
-
content:
-
概述:指定具体的元数据值,通常搭配name或http-equiv属性一起使用。
-
-
<title>
-
概述:表示网页的标题,显示在浏览器的标签栏上,也会在搜索引擎结果中作为网页的链接标题。
-
属性:<title>标签本身并没有许多属性,没有常见的class或id这样的常见属性。这个标签的内容应该简洁、明确。
<body>
-
概述:网页的主体内容,也是用户真正意义看到的部分。
<h1>、<h2>、<h3>….
-
概述:<hn>系列标签通常用来表示主题内容部分的文本的标题,后面的数字越大层级越小,字体也越小。
-
常见属性:
-
class
-
作用:为标题元素指定一个或多个类名,通常与CSS或JavaScript结合使用,来改变标题的样式或行为。
-
-
id
-
作用:为标题元素指定一个唯一标识符,用于在CSS、JavaScript中进行操作,或在链接中定位到标题。
-
-
style
-
作用:为标题元素提供内联样式,直接控制标题的外观。
-
-
lang
-
作用:指定标题内容的语言,用于国际化(i18n)和搜索引擎优化(SEO)
-
-
title
-
作用:为标题提供额外的描述,通常在鼠标悬停时显示。
-
-
<p>
-
概述:
-
将一段文字或其他元素定义为一个段落,浏览器会自动在段落之间加入垂直的距离,让内容更方便阅读。
-
每个段落会自动换行,并在段落末尾插入换行。
-
段落内部可以包含其他内联元素,如链接、图片、强制样式等,也可以包括一些文本格式化标签,如<strong>、<em>等。
-
-
常见属性:<p>标签本身没有太多专用属性,但是可以使用HTML中的通用属性,如:
-
class
-
id
-
style
-
lang
-
title
-
文本格式化标签
-
概述:
-
文本格式化标签用来控制文本的显示方式与样式,改变文本外观,使内容更加易读、可访问、结构清晰。
-
通常不会影响网页的结构(比如网页的布局),而是调整内容的呈现方式。
-
-
常见的文本格式化标签:
-
<hr>:显示一条直线
-
<br>:在文本前添加换行
-
<strong>:表示强调文本,默认情况以粗体显示
-
<b>:对字体加粗,但与<strong>标签不同之处在于<strong>标签会在SEO中具有一定的权重,<b>标签不能影响SEO。
-
<i>:将文本显示为斜体
-
<u>:在文本下方添加下划线
-
<mark>:对文本进行高亮
-
特殊字符
一些字符本身是HTML语言的关键字等,为了显示这些特殊字符,而不被浏览器忽略或解析,需要使用特殊字符。
-
空格: 
-
大于:>
-
小于:<
-
引号:"
-
版权符号@:©
CSS
CSS由选择器(Selector)和声明块(Declaration Block)组成
/*选择器 {属性 :值;}*/ p{ color: blue;/*文字颜色*/ font-size: 16px;/*文字大小*/ }
p是选择器,表示对<p>标签应用样式
color和font-size是CSS属性
blue和16px是属性的具体值
CSS引入方式
有三种方式将CSS添加到HTML页面:
外部样式表(推荐)
<link rel="stylesheet" href="style.css">
rel属性:定义文档和外部资源之间的关系
常见的rel值:
-
stylesheet:表示链接的文件是一个样式表CSS
-
icon:用于指定网页的图标文件,通常用于设置网页的favicon–网站图标
-
alternate:表示一个备用的资源,比如不同语言的版本
-
prefetch:用于指示浏览器在空闲时预加载某些资源
当浏览器遇到<link rel=“stylesheet”>标签时,它会知道有一个外部CSS文件,会将文件的样式应用到当前HTML文件中
使用外部样式表优点:
-
结构与样式分离,代码清晰,易于维护
大部分企业级Web开发都使用这个模式
内部样式表
<style> body{background-color:blue;}/*body标签的背景设置为blue*/ </style>
适用于单个HTML页面的小型项目
内联样式
<p style="color: red;">红色文本</p>
仅用于个别元素的临时样式,不适用复杂项目
选择器
选择器用于指定HTML元素的样式,常见的选择器包括:
选择器 | 作用 | 示例 |
---|---|---|
元素选择器 | 选中所有该类型的元素 | p{color: blue;} |
类选择器 | 选中所有class=“…”的元素 | .btn{background: red;} |
ID选择器 | 选中id=“…”的唯一元素 | #header{font-size: 20px;} |
后代选择器 | 选中某个元素内的指定子元素 | nav a{text-decoration: none;} |
伪类选择器 | 选中元素的特定状态 | a : hover{color: green;} |
例子:
/*选中所用p元素*/
p{
color: blue;
}
/*选中所有class="btn"的元素*/
.btn{
background-color:red;
}
/*选中id="header"的元素*/
#header{
font-size: 20px;
}
/*选中所有nav内的a元素*/
nav a{
text-decoration: none;
}
/*选中鼠标悬停的a标签*/
a:hover{
color: green;
}
3.JavaScript
1.基本概念
JavaScript是一种解释性语言,由浏览器负责解释执行,并不需要再安装单独的解释器
JavaScript主要用于:
- 网页的动态操作
- 获取提交的表单
JavaScript是动态类型语言,声明变量时并不需要明确指定变量的数据类型
JavaScript通常嵌入到HTML的<script>标签中实现功能
2.网页引入js文件的方式
-
内部js:在网页中的任何位置加入<script>标签
<script> alert(1)(弹出对话框) </script>
-
外部js,创建.js文件,利用<script src=“js文件路径”>
<script src="js路径"></script>
-
事件型引入:在标签中键入事件对应的属性
<button onclick="alert('aaa');">button</button>
-
<a>标签引入js脚本
<a href="javascript:alert('example');"></a>
这种引入方式也称为JS伪协议
伪协议:
-
实际上javascript:是一种特述的协议,它利用了URI协议的机制
-
javascript:协议让浏览器执行紧随其后的JavaScript代码
-
不推荐使用javascript:协议
-
更推荐使用JavaScript事件绑定而不是在href属性中直接写JavaScript代码
-
DOM——文档对象模型
-
是HTML或XML文档的编程接口,将网页结构化为一颗树状结构。
-
每一个HTML标签都创建一个对应的对象,所有的这些对象都可以通过JavaScript访问。
-
这些对象按照树形数据结构组织,称为DOM树,DOM数中:
-
每一个HTML元素都是一个节点Node
-
根节点是<html>子节点是<head>和<body>
-
文本、属性也是节点
-
<!DOCTYPE html> <html> <head> <title>DOM 树示例</title> </head> <body> <div id="container"> <p>Hello, World!</p> </div> </body> </html>
-
-
浏览器解析HTML时,会构建这棵DOM树,使JavaScript可以访问和操作HTML页面
-
-
document与根节点
-
document是整个DOM树的入口,代表了HTML或XML文档的顶层对象,在JavaScript中可以通过document对象访问和操作整个HTML页面
-
doucument的特点:
-
是DOM API提供的全局对象,可以在浏览器环境中直接俄访问
-
document不是一个普通的DOM元素,而是Document类的对象,继承自Node。
-
document提供了许多方法来访问和操作DOM(获得节点对应的对象,方便JavaScript操作),比如:
-
document.getElementById(“id”):通过ID获取元素
-
document.querySelector(“selector”):通过CSS选择器获取元素
-
document.creatElement(“tag”):创建新元素
-
document.body:访问<body>元素
-
document.doucumentElement:访问根节点通常是<html>
-
-
-
根节点 Root Node:
-
是整个DOM树的顶层节点,是所有DOM元素的最终父节点,在HTML文档中,通常是<html>元素
-
-
对比项 document 根节点 本质 DOM API提供的全局对象 DOM树的实际根元素 数据类型 Document对象 Element(通常是<html>) 是否有父节点 无 有,document指向它 -
document是JavaScript提供的Document对象,用于访问整个DOM树,类似于DOM树的入口点
-
根节点是document.documentElement,通常是<html>元素
-
document不能直接用于渲染,而根节点<html>是可见的
-
在JavaScript操作DOM时,document主要用于获取、创建和操作DOM,而documentElement主要用于访问<html>及其属性
-
事件与事件传播
基本概念
-
事件:在程序运行过程中,某个操作,或状态的变化,例如用户点击、鼠标移动、数据加载完成、消息到达等,触发相应的处理动作。
-
事件驱动编程(Event-driven Programming):基于事件的触发机制,让程序响应外部或内部事件
-
前端事件:
-
DOM事件:
-
点击 click
-
悬停:mouseover
-
输入:input
-
键盘按下:keydown
-
……
-
-
-
后端中的事件
-
异步事件处理:
-
消息队列,如:Kafka、RabbitMQ
-
事件总线 Event Bus
-
Spring的事件机制
-
-
回调和通知:后端系统可能在某个任务完成时发出事件或通知,从而触发其他模块的处理
-
回调函数
什么是回调函数?
回调函数是一种特殊的函数,作为参数传递给其他函数,并在一定条件(适当的时机)下被调用。是函数式编程的重要概念。
作用:
核心作用被称为**解耦代码逻辑**,意思是使得函数可以动态的执行不同的代码,典型应用包括:
- 事件处理(如addEventListener)
- 异步编程(如AJAX请求、定时器)
- 高阶函数(如map、filter、forEach)
- 错误处理(如Node.js的回调模式)
回调函数的基本用法
-
传递回调函数
function doSomething(callback){
console.log("执行某些操作");
callback(); //调用回调函数
}
function myCallback(){
console.log("回调函数被执行");
}
//传递回调函数
doSomething(myCallback);
当mycallback作为参数传递给doSomething时,并不会立即执行,只有当doSomething执行到callback()时才会执行myCallback()
使用匿名函数作为回调函数参数传递
function doSomething(callback){
console.log("执行某些操作");
callback();
}
doSomething(function(){
console.log(匿名回调函数被执行);
} );
回调函数在事件处理中的作用 在addEventListener中使用回调函数
document.getElementById("一个html标签的id").addEventListener("click",function(){
console.log("想要回调函数实现的功能");
});
监听器Listener
1.监听器:
-
是事件驱动编程的核心概念,指在特定事件(如点击、键盘输入、网络请求完成等)发生时被触发的函数。
-
在JavaScript中,监听器通过addEventListener()方法注册到DOM元素或其他事件源上。注册:
-
将函数(监听器)和某个事件挂钩的过程,类似告诉浏览器当事件发生请执行这个事件。
-
function sayHello(){ console.log("hello") } const button = document.querySelector("button");//通过CSS样式表获取button标签 button.addEventListener("click",sayHello);//完成注册
-
-
当事件发生时,浏览器就会主动调用注册的函数。
-
在上述的代码中,回调函数sayHello就是挂钩在<button>元素上的监听器,而button.addEventListener()方法实现了监听器的注册。
-
addEventListener()方法的三个参数:
-
表示事件的字符串
-
表示监视器的回调函数
-
useCapture:
-
默认为false,表示监听器在冒泡阶段执行
-
true:表示监视器在捕获阶段执行
-
-
-
匿名函数包装:
-
匿名函数是没有名字的函数,通常适用于一次性的场景,可以直接定义并传递给其他函数
-
-
-
-
//普通函数 function sayHello(){ console.log("Hello!"); } //匿名函数 () => { console.log("Hello!"); }
-
当需要传递参数给回调函数时,如果直接写函数名+(参数)的形式,会变成对函数的调用,会让回调函数直接执行,但实际上我们希望的是传递对函数的引用,希望回调函数到适当时机再执行,通过匿名函数包装,可以延迟函数的执行,并在需要时传递参数。
function onClick(message){
console.log(message);
}
//错误写法:onClick会立即执行
butn.addEventListener("click",onClick("像传递的参数"));
//正确写法:使用匿名函数包装
butn.addEventListener("click",() => {
onClick("想传递的参数")
});
-
-
-
实际上就是声明一个匿名函数,将匿名函数引用作为参数传递,再匿名函数中执行对真正想要的函数的调用。
-
-
-
监听器与回调函数:
-
监听器是事件专用的回调函数
-
所有监听器都是回调函数,但并非所有回调函数都是监听器(如setTimeout的回调函数)。
事件模型
所谓的传播,本质上就是浏览器对事件的分发,当事件发生时,浏览器按照设计的顺序,依次调用DOM树不同层上的监听器,从逻辑上看,就像事件沿着DOM树传递
-
事件模型:事件在浏览器中传播、分发和处理的机制
-
事件目标:事件的触发元素,例如,被点击的按钮或链接
-
事件传播:事件从事件源传播到DOM树中的其他元素。有三个阶段:
-
捕获阶段 Capture Phase:从window对象开始,向事件目标元素传播
-
目标阶段 Targer Phase: 事件到达目标元素,通常在这个阶段执行与该元素相关的事件处理程序。实际上,目标阶段从逻辑上将更像一个点,或是一条交界线,事件遵循捕获阶段的顺序从上到下传播到了目标节点,目标节点先执行捕获阶段的监视器,之后进入冒泡阶段,开始执行目标元素的冒泡阶段
-
冒泡阶段 Bubble Phase: 事件从目标元素向上冒泡(传播),直到document或根元素,直到没有监听器为止
-
三层<div>举例:
-
以一个三层嵌套的div模型来分析事件流:
<div id="grandparent" >
祖辈
<div id="parent" style="color: blue">
父辈
<div id="child" style="color: khaki">
孩子
</div>
<div id="brother">
兄弟
</div>
</div>
</div>
-
利用addEvetntListener()方法,在三个层次的div中设置两个监听器对应捕获阶段与冒泡阶段
<script>
let grad = document.getElementById("grandparent");
let parent = document.getElementById("parent");
let child = document.getElementById("child");
let brother = document.getElementById("brother");
function onClick (message){
console.log(message)
}
//捕获阶段监听器注册,true参数表示监听器在捕获阶段工作
grad.addEventListener("click",() => {
onClick("this is grad Catch");
},true);
parent.addEventListener("click",() => {
onClick("this is parent Catch");
},true);
child.addEventListener("click",() => {
onClick("this is child Catch")
},true);
brother.addEventListener("click",() => {
onClick("this is brother Catch");
},true);
grad.addEventListener("click",() => {
onClick("this is grad Boob");
});
parent.addEventListener("click",() => {
onClick("this is parent Boob");
});
child.addEventListener("click",() => {
onClick("this is child Boob");
})
brother.addEventListener("click",() => {
onClick("this is brother Boob");
});
</script>
当点击gradpaernet<div>:
-
事件发生
-
根据事件传播模型,开始捕获阶段,从window对象–>document对象–>dom树直到到达目标元素gradpaernt<div>
-
所以,此时grdaparent<div>的捕获阶段的监听器被触发,控制台输出了“this is grad Catch”
-
-
理论上到达gradparent<div>时,进入了传播模型中的目标阶段,但实际上目标阶段从逻辑上将就是一个时间点,马上进入了冒泡阶段
-
根据冒泡阶段的模型,事件从目标元素开始,沿着DOM树往上传播,直到到达window对象
-
因此,gradparernt<div>的部署在冒泡阶段的监听器在整个点击事件传播的冒泡阶段第一个执行。
-
-
同样的道理,当点击位于第三层的child<div>时,整个点击事件的传播仍然遵循捕获阶段 =》目标阶段=》冒泡阶段的顺序进行传播
-
因此,当点击事件发生:
-
捕获阶段:事件首先传播到grad<div>,grad<div>监听器被触发,依次事件到达parent<div>,触发parent<div>监听器,最后到达目标元素child<div>,触发目标元素的针对捕获阶段的监听器
-
控制台输出顺序:“this is grad Catch”=>“this is parent Catch”=>“this is child Catch”
-
冒泡阶段:事件从child<div>开始传播,沿着DOM树向上依次传播,依次触发每个层的冒泡阶段监听器
-
控制台输出顺序“this is child Bood”=>“this is parent Boob” =>“this is grad Boob ”
-
-
对于处于同一DOM树层次的div节点,在chid<div>之后,增加一个brother<div>,发现即使在编写代码时brother<div>在child之后,仍然不会触发child<div>的监听器,可见,事件并不会在同一层DOM树内传播
对于不使用addEventListener()方法的监听器的情况:
1. 内联:直接在标签中使用添加属性的方式绑定监听器——内联(inline)
<button onclick = "butOnClick()">click</button>
<script>
function butOnCilck(){
console.log("被点击了!");
}
</script>
-
这种模式不符合w3c规范,行为与内容没有分离,但是绑定速度快,跨浏览器性能较好
2.动态绑定:对于具体的元素进行动态绑定—脚本模型
<div id ="gradM0">
祖辈动态绑定
<div id="parM0">
父辈动态绑定
<div id="chiM0">
子辈动态绑定
</div>
</div>
</div>
<script>
let gradM0 = document.getElementById("gradM0");
let parM0 = document.getElementById("parM0");
let chiM0 = document.getElementById("chiM0");
gradM0.onclick = () =>{
onClick(" this is gradM0");
};
parM0.onclick = () => {
onClick("this is parMO");
};
chiM0.onclick = () =>{
onClick("this is chiM0");
};
</script>
-
可见,对于动态绑定的监听器,只在事件传播的冒泡阶段才能触发。
-
动态绑定还有一个缺点:一个元素对于同一类型的事件只能注册一个监听器,注册的最新的监听器会覆盖之前的监听器
-
如果使用addEventListener()方法注册监听器,可以为同一元素添加多个针对统一事件同一阶段的监听器,此时,由代码中注册的顺序决定监听器的触发顺序
-
<button id="button02">addEventListener按纽</button> <script> let button02 = document.getElementById("button02"); button02.addEventListener("click",() => { onClick("hello again Linstener"); }); button02.addEventListener("click",() =>{ onClick("hello Linstener"); }); </script>
事件传播机制的应用
阻止事件的传播
-
JavaScript提供了两个方式用来阻止事件的传播
-
event.stopPropagtion():在捕获阶段或者冒泡阶段阻止事件的传播
-
event.stopImmediatePropagation():不止阻止事件朝下一层传播,还阻止事件在同一元素的同一阶段的不同监听器之间传播
-
<div id="stopParent"> 被阻止的父辈 <div id="stopChild"> 被阻止的孩子 </div> </div> <script> let stopParent = document.getElementById("stopParent"); let stopChild = document.getElementById("stopChild"); stopParent.addEventListener("click",(event) =>{ console.log("可能被阻止的父辈的Catch"); // event.stopPropagation();//阻止事件捕获 },true); stopParent.addEventListener("click",(event)=>{ console.log("可能被阻止的父辈的Bubble"); }); stopChild.addEventListener("click",(event)=>{ console.log("可能被阻止的孩子的Catch01") },true); stopChild.addEventListener("click",()=>{ console.log("可能被阻止的孩子的Catch02") },true); stopChild.addEventListener("click",(event) =>{ console.log("可能被阻止的孩子的bubble01"); //event.stopPropagation();//阻止事件冒泡 // event.stopImmediatePropagation();//阻止事件传播到同一元素其他监听器 }); stopChild.addEventListener("click",() =>{ console.log("可能被阻止的孩子的bubble02"); }); </script>
-
event.stopPropagation();//从父节点阻止事件捕获
-
-
此时,从父节点使用stopProragation()方法时虽然child才是目标元素,但是事件传播被阻断了,只能触发父节点的部署在捕获阶段的监视器,冒泡阶段完全没有进行
-
event.stopPropagation();//阻止事件冒泡
-
此时在孩子的第一个冒泡阶段的监听器中传递event参数并使用stopPropagation()方法,捕获阶段没有受到影响,,孩子的冒泡阶段的两个监听器都按照注册顺序正常触发,但是事件的传播到此为止,父节点的冒泡阶段监听器没有触发
-
event.stopImmediatePropagation();//阻止事件传播到同一元素其他监听器
-
-
此时,在chid的冒泡阶段01监听器中调用了event.stopImmediatePropagation()方法,可以发现,捕获阶段没有受到影响,冒泡阶段只能触发child的冒泡阶段01监听器,冒泡阶段的02监听器无法触发,证明传播在这个监听器中被阻止了。
-
-
利用stopImmediateProgation()和stopPropagation()方法可以非常自由准确的阻止事件传播
-
阻止事件传播的主要意义在于控制事件的行为,避免不必要的事件触发或者产生冲突,比如:
-
防止事件冒泡:当目标元素在子层时,由于传播模式,父节点的监听器也会被触发,这是逻辑上不希望的
-
阻止默认行为:某些元素如<a>和<form>存在一些默认的行为,如跳转、提交,利用event.preventDefalt()方法阻止默认行为同时配合stopPropagation()控制事件传播
-
const link = document.querySelector("a");//利用CSS控制器获取<a>元素 link.addEventLinstener("click",(event) =>{ event.preventDefalt();//阻止<a>元素的自动跳转 event.stopPropagation();//阻止事件传播 console.log("点击链接后不发生跳转"); });
-
-
如果不使用preventDefalt方法<a>会自动跳转,利用addEventListener()方法添加的监听器也会执行(可以看到控制台恒快的显示输出)但是页面仍然会跳转。
-
-
优化性能:在复杂的DOM结构中,事件冒泡可能导致大量不必要的监听器被触发,影响性能
-
避免事件冲突:对于同一元素的部署在相同阶段的多个监听器,当事件发生时会全部触发,为了避免逻辑上的冲突,可以使用stopImmediateProgation()方法限定事件只传播到需要的监听器
-
事件代理——Event Delegation
-
事件代理的原理
-
一种利用事件冒泡机制来优化事件处理的技术
-
核心思想是:将监听器绑定到父元素上,而不是直接绑定在每个子元素上。通过这种方式,可以同一管理多个子元素事件,减少内存占用并提高性能
-
-
事件代理的实现方式
-
将事件监听器绑定在父元素而不是每个子元素
-
当子元素触发事件,事件会冒泡到父元素
-
在父元素监听器中,利用event.target获取实际触发事件的子元素,执行响应的逻辑
-
<div id="EVentDelegation">
<ul id="list"> <!-- 表示无序列表-->
<li id="row1">item1</li>
<li id="row2">item2</li>
<li id="row3">item3</li>
</ul>
</div>
<script>
list = document.getElementById("list"); //只操作父元素
list.addEventListener("click",(event) => {
if(event.target.tagName === "LI") //target代表真正触发事件的元素
{
console.log("Clicked:",event.target.textContent);//textContent为元素对应的标签中的文本信息
}
});
</script>
事件代理的关键点:
event.target:指向实际触发事件的元素
event.currentTarget:指向绑定事件监听器的元素
注意在代理监听器中应该始终检查触发事件的元素是否为真正需要的元素(if(event.target.tagName === “LI”))
事件代理的应用场景:
列表或表格:例如点击列表或表格行时触发事件
动态内容:例如动态添加的按钮或链接
性能优化:当子元素较多时,事件代理可以显著减少事件监听器的数量
事件代理的优势:
减少内存占用:只需要绑定一个监听器而不需要为每个子元素绑定
动态元素支持:即使子元素是动态添加的,也不需要重新绑定监听器
代码简洁:逻辑集中在父元素上,便于维护
事件代理的局限性:如果子元素的事件被阻止冒泡,事件代理将无法捕获该事件
数据库知识学习
1.1数据库基础
现代软件几乎离不开数据库,但是对数据库具体概念并不是每个人都清楚。
有时在数据库方面的同一个术语会代表不同内容
1.1.1数据库
数据库:以某种有组织的方式存储的数据集合
类似于一个文件柜,数据库就是这个柜子,对于柜子装什么内容、如何装内容柜子本身并不在意。
数据库管理系统——DBMS:数据库的管理软件,通过DBMS创建和管理数据库,常见的MySQL、Oracle
1.1.2表
表:
-
一种结构化的文件,存储某种特定类型的数据。
-
存储在同一张表上的数据应该是同一种类型。
-
在一个数据库中,表名应保持唯一
-
模式:关于数据库和表的布局及特性的信息,包括:
-
如何存储数据
-
数据如何分解
-
各部分信息如何命名
-
…..
-
1.1.3 列和数据类型
表由列组成。
列(column):
-
表中的一个字段,所有表都是由一个列或多个列组成。
-
应当正确的把数据分为多个列,根据需求确定分到何种程度
-
每个列都有固定的数据类型,数据类型限制了存储在每一列的数据的种类
-
由于不同的DBMS对数据类型的定义不同,导致了不同DBMS的SQL不兼容
1.1.4 行(row)
表中的数据按照行存储。
行也称为记录,一条记录代表了一个具体的数据对象。
行与列示意图
1.1.5 主键
对于一张表的每一个记录,都应该有一列(或多列)用来对该记录进行唯一标识, 这一列称为主键(primary key)。
-
SQL语法没有强制规定创建一张表必须有主键,但是规范设计的表都必须考虑设计主键
-
任意两行都应该有不同的主键值,也就是作为主键的列中的值都是唯一的
-
每一行都必须有一个主键值,主键值不能为NULL
-
主键列中的值不允许修改或更新
-
主键值不能重用—-即使某行被删除,它的主键值也不能赋给新行。
-
如果多个列组合作为主键,那么单个列的值可以不唯一,但多个列的组合必须唯一
1.2 什么是SQL
SQL:结构化查询语言,是一种专门用于沟通数据库的语言
SQL相比其他计算机语言语法更加简洁内容少,因为SQL的设计目的就是为了完成一项任务——从数据库读写数据。
数据结构知识学习
第一章 数据结构绪论
1.1 数据结构起源
数据结构是一门研究非数值计算的程序设计问题中的操作对象,以及它们之间的关系和操作等相关问题的学科
1968年,高德纳编写《计算机程序设计艺术》标志数据结构这门学科的正式诞生
程序设计 = 数据结构+算法
1.2基本概念和数据
1.2.1数据
数据:描述客观事物的符号,是计算机中能操作的对象,是能被计算机识别并输入给计算机进行处理的符号集合。
数据—-就是符号,并且:
-
可以输入到计算机
-
可以被计算机程序处理
-
数据可以大的分为:
-
数值型:整型、实型等,计算机可以对这些数据进行数值计算
-
非数值型:典型的字符型数据(常见的图像、视频等可以转换成字符型数据),计算机需要对这些数据进行非数值处理。
-
1.2.2 数据元素
数据元素:组成数据的,有一定意义的基本单位,在计算机中通常作为整体处理,也被称为记录(联想数据库表中的一行)
1.2.3 数据项
一个数据元素可以由若干数据项组成,比如,将人作为数据元素考虑,可以包含:
-
眼睛
-
手
-
耳朵
-
。。。
也可以包含:
-
姓名
-
职业
-
身份证号
-
。。。。
具体要有哪些数据项,应该由你的系统来决定。
数据项是不可分割的最小单位。
1.2.4 数据对象
数据对象:是性质相同的数据元素的集合,是数据的子集。
性质相同:指数据元素具有相同数量和类型的数据项。(小明:学号,性别,年龄
小红:学号,性别,年龄 )
小明小红等的组合,构成了学生这一数据对象
数据对象是数据的子集,有时可以把数据对象简称为数据
1.2.5 数据结构
结构—-即是关系
不同的数据元素之间不是独立存在的,而是存在一定的关系,这种关系称为结构。
数据结构:相互之间存在一种或多种特定关系的数据元素的集合
1.3 逻辑结构与物理结构
逻辑结构:数据对象中数据元素之间的相互关系,包括:
-
集合结构:
-
集合结构中的元素除了属于同一集合外,没有其他关系。
-
集合中每个元素都是平等的。
-
-
线性结构:
-
元素之间的关系是一对一的
-
-
树形结构:
-
元素之间存在1对多的层次关系
-
-
图形结构:
-
元素之间是多对多的关系
-
物理结构:
-
也成为存储机构,指数据的逻辑结构在计算机中的存储形式
-
数据结构的存储结构主要是在内存上实现的,外村上一般使用文件结构来存储数据
-
顺序存储结构:
-
把数据元素存放在地址连续的存储单元,其存储的数据元素之间的逻辑结构和物理位置保持一致
-
典型的顺寻存储结构:数组,计算机在内存中开辟固定大小且连续的内存单元来存储数组
-
-
链式存储结构:
-
把数据元素存储在任意的存储单元。数据元素的存储关系不能反应其逻辑关系。
-
依靠指针存放有逻辑关系的数据元素的地址
-
逻辑结构是面向问题的,存储结构是面向计算机的。物理结构的主要目的就是将数据和数据的逻辑结构正确的存储到计算机内存中。
1.4数据类型
数据类型:
-
一组性质相同的值的集合以及定义在此集合上的一些操作的总称。
-
数据类型按照值的不同进行划分。
-
数据类型说明了变量(或表达式)的取值范围以及能进行的操作。
-
例如:c语言中声明变量:
int a,b;
-
说明了:给a和b赋值时不能超出int型的取值范围,a和b之间只能进行int类型允许的运算。
不同的计算机有不同的硬件系统,这些硬件系统对数据类型的具体实现可能存在差别。
但在设计计算机程序,研究数据结构时,这些具体实现并不重要,而应该专注于数据的逻辑上的含义。
抽象—取出事物具有的普遍性的本质
抽象数据类型 ——ADT:
-
一个数学模型和定义在该模型上的一组操作。
-
抽象数据类型的定义仅取决于它的一组逻辑特性,与计算机内部的表示和实现无关
-
抽象数据类型体现了程序设计中问题分解、抽象、和信息隐藏的特性。
-
ADT标准格式:
ADT 抽象数据类型名
Data
数据元素之间的逻辑关系的定义
Operation
操作1
初始条件
操作结果描述
操作2
.。。。
操作n
。。。。
endADT
第二章 算法
算法与数据结构是不可分割的,单纯研究数据结构不去考虑算法,没有意义。
2.1 算法定义
算法:解决特定问题的求解步骤的描述,在计算机中变现为指令的有限序列,每个指令表示一个或多个操作。
2.2算法的特性
算法有5个基本特性:输入、输出、有穷性、确定性和可执行性
-
输入输出
-
算法可以有零个多个输入
-
但算法至少有1个或多个输出
-
-
有穷性:
-
算法会在执行有限的步骤后自动结束不陷入无限循环
-
在实际设计算法时,不止是数学上的有穷性,更应该是可接受、合理的边界
-
-
确定性:
-
算法的每一步骤都有确定的含义,不会有二义性。
-
-
可执行性:
-
算法的每一步都必须是可执行的,也就是每一步都能通过有限的执行次数完成
-
2.3 算法设计的要求
一个好的算法,应该满足:正确性、可读性、健壮性、时间效率高和存储量低
-
正确性:算法的正确性分为几个层级:
-
算法程序没有语法错误
-
算法程序对于合法输入数据,能够产生满足需求的输出结果
-
对于非法的输入数据,能够得到满足规格说明的结果
-
对于各种测试数据都能有满足要求的结果
-
-
可读性:算法的设计应该便于阅读、理解和交流
-
健壮性:好的算法能够对不合法数据输入有合理的处理
-
时间效率高和存储量需求低:
-
时间效率高指的是算法的执行时间
-
存储量需求指的是算法在执行过程中需要的最大存储空间
-
2.4 算法的度量方法
一般来讲算法提高效率都是提高时间效率
2.4.1 事后统计方法
所谓事后统计方法指的是:通过设计好的测试程序和数据,对不同算法编制的程序的运行时间进行比较,从而确定算法效率的高低。
这种方法有很大的缺陷:
-
必须依赖事先编写好程序,如果要测试的算法本身就很糟糕,这种行为就没有意义
-
时间的比较依赖计算机硬件和软件等因素影响,并不可靠
-
完全可行、中立的测试数据设计比较困难
因此,评价算法效率时不适用这种方法
2.4.2 事前分析估算方法
定义:在计算机程序编写前,依据统计方法对算法进行评估。
一个高级程序语言编写的程序在计算机上运行所消耗的时间取决于以下因素:
-
算法采用的策略
-
编译产生的代码质量
-
问题的输入规模
-
机器执行指令的速度
忽略掉与计算机软件、硬件相关的因素,一个程序运行时间的长短,从本质上来说,依赖于算法的好坏和问题的输入规模(输入量的多少)。
例:两种求和算法:
nt i = 0,sum = 0, n = 100; /*执行1次*/
for(i=1;i<=100;i++) /*执行n+1次*/
{
sum+=i; /*执行n次*/
}
print("%d",sum); /*执行1次*/
这个程序执行总次数T: T=1+n+1+n+1=2n+3次
第二种:
int i = 0, sum = 0, n = 100; /*执行1次*/
sum = (n+1)*n/2; /*执行1次*/
print("%d",sum); /*执行1次*/
这个程序总执行次数T=1+1+1=3次
可见,测定运行时间最可靠的方法就是计算对运行时间有消耗的基本操作的执行次数。
因此,在分析程序的运行时间时,最重要的就是把程序看作独立于程序设计语言的一系列步骤
2.5 函数的渐近增长
定义:给定两个函数 f(n),g(n),若存在一个正整数N,当n>N时,总有f(n)>g(n),则称f(n)的增长渐近快于g(n)
例如:假设两个算法的输入规模都是n,算法A要做2n+3次操作,算法B要做3n+1次操作。
次数 | 算法A 2n+3 | 算法A* 2n | 算法B 3n+1 | 算法B* 3n |
---|---|---|---|---|
n=1 | 5 | 2 | 4 | 3 |
n=2 | 7 | 4 | 7 | 6 |
n=3 | 9 | 6 | 10 | 9 |
n=10 | 23 | 20 | 31 | 30 |
n=100 | 203 | 200 | 301 | 300 |
-
当n>3时,算法A的次数总是小于算法B的,因此算法B的渐进增长快于算法A
-
随着n的值越来越大,算法A与A*、B与B*之间的由常数项造成的差距相对来说影响越来越小,可以说,我们最终可以忽略这些加法常数。
实际上,不止是常数项,随着规模的进一步扩大,最高次项的系数的影响也越来小。
可以认为,最高次的指数越大,函数随着n的增长也会越来越快
判断一个算法的效率时,函数中的常数项和其他次数项都可以忽略,只需要关注最高阶项的阶数。
2.9 时间复杂度
定义:在算法分析中,语句的总执行次数T(n)是关于问题规模n的函数,算法的时间复杂度,就是算法的时间度量,记作:
T(n)=O(f(n))
表示:随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同
可以把f(n)理解成:关键语句执行次数,而O(n)是这个函数的增长率的一种表示方法。
推导大O阶O(n):
-
将f(n)中的所有常数项替换为1
-
只保留最高阶项
-
将最高阶项系数改为1
-
得到大O阶。
大O阶推导:
常数阶
int i = 0, sum = 0, n = 100; /*执行1次*/
sum = (n+1)*n/2; /*执行1次*/
print("%d",sum); /*执行1次*/
执行次数函数:f(n) = 3
按照推导大O阶的方法推导:
-
把常数项转成1 ==>O(f(n))=1
-
得到大O阶
因此,若执行次数函数为常数函数,那么无论具体的常树值为多少,时间复杂度都为O(1)
对数阶
int count = 1;
while(count<n){
count = count *2;
}
-
分析这个程序的执行次数函数:
次数 count 1 2*1=2 2 2*2=4 3 2*2*2=8 … … i 2i 假设当n=x(次数)时count = n,即:
2x=n x=log2n
所以,
f(n) = log2n
故,O(f(n))=logn
类似的,通过对循环语句的分析,推导出程序的执行次数函数,从而推导出程序的时间复杂度。
常见的大O阶(时间复杂度):
O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(n3)<O(2n)<O(n!)<O(nn)
2.10 最坏情况与平均情况
假设一个顺序存储的数组,我们去查找这个数组时有可能第1个元素就是我们需要的情况,这时算法时间复杂度为O(1),也有可能到最后一个元素才找到,这时,算法时间复杂度就为O(n),此时被称为最坏情况。
一般算法中提到的运算时间都指的是最坏情况运行时间。
平均运行时间:从概率的角度看,数组的元素储存遵循顺序分布,元素被找到的概率都为1/n,
此时,平均查找时间(查找次数,可以理解为if(a[i]==n)的执行次数)也就是期望:
X:查找次数的随机变量,取值{1,2,3,。。。。。n-1}
E(X) = 1/n *Σxi=(1+2+3+4+….+n-1)/n=1/2n
此时的时间复杂度称为平均时间复杂度=O(1/2n) = O(n)
研究平均时间复杂度很有意义,因为它是期望运行时间。
但一般情况没有特别说明时,我们求得都是最坏时间复杂度。
2.11 算法空间复杂度
有时,算法可以做到用空间换时间,例如:
问题是判断一个年份是否是闰年:
-
每次给定一个年份→利用计算判断是否是闰年
-
建立一个大数组,数组中按下标对应很长一段时间的年份,如果该年是闰年数组元素计为1,其余记为0,这样,判断一个年份是否是闰年只用取出数组对应年份的元素就好。
这样以空间换取时间的设计方法是否优异,要根据具体情况具体分析。
算法的空间复杂度 :
S(n) = O(f(n)):
-
n:问题的规模
-
f(n):算法关于n所占据存储空间的函数
大部分情况如果不明确指代,”算法复杂度“都指的是时间复杂度。
总结
第一周的学习主要精力放在了前端知识尤其是javascript事件传播模型的学习,以及各种集成开发工具的安装中,对CSDN的文字编辑也不是很熟练,格式显得很混乱,下一周开始将开始学习更加具体的知识。
本周阅读书籍:
《Java核心技术卷Ⅰ》
《MySQL必知必会》
《图解HTTP》
《大话数据结构》