介绍完了 Cookie 和 Session 之后,我们现在来看看二者的区别,当然最大的区别前面已经反复强调过了:Cookie 数据存放在客户的浏览器上,Session 数据放在服务器上;上面的区别也导致了 Cookie 和 Session 之间安全性的差别。Cookie 数据保存在用户端,数据容易被窃取,不太安全。虽然对 Cookie 中的敏感信息加密可以暂时规避安全风险,但即使加密数据被泄露了总归是不好的;Session 数据存储在服务器,可以有效规避信息泄露问题;Cookie 和 Session 之间生命周期不同,具体可以看前面两个的介绍;单个 Cookie 保存的数据不能超过4K,很多浏览器都会限制一个站点最多保存20个 Cookie,且只能保存字符串内容;对于 Session 则没多少限制,Session 可以保存复杂的数据类型,而且数量没有限制,主要是访问用户太多后,容易造成服务器内存溢出。
前面有提到,android:background也可以直接设置成图片,那么在设置成图片的场景下,它和android:src是否是一样的呢?我们来进一步考察一下:<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="#CC1010" android:src="@drawable/image" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" android:background="@drawable/image" /> <ImageView android:layout_width="200dp" android:layout_height="200dp" android:layout_gravity="bottom" android:background="#CC1010" android:src="@drawable/image" /> <ImageView android:layout_width="200dp" android:layout_height="200dp" android:layout_gravity="bottom|right" android:background="@drawable/image" /></FrameLayout>在上面的代码中,我们在屏幕的 4 个角放置了 4 个 ImageView ,分别从尺寸大小、图片设置方式两个维度做了区分,为了方便对比,在使用android:src属性之后,通过android:background加了红色的背景,效果如下:我们首先看上排的两个图片效果:长宽均为wrap_content左边一个用的android:src右边一个用的是android:background可以看到两者效果完全一样,再来看下排的两个图片:长宽均为200dp左边一个用的android:src右边一个用的android:background可以清楚的看到,在将 ImageView 的大小固定之后,左边的图片内容仍然保持原始比例,并且露出 ImageView 的红色背景(说明 ImageView 大小和我们设置的一样),而右边的图片会拉伸图片大小直至占满整个 ImageView。这样一来就可以得出结论:在 ImageView 的尺寸和图片尺寸比例一致的情况下,使用android:background设置图片和使用android:src效果一样在 ImageView 的尺寸和图片尺寸比例不一致的情况下,使用android:src会保留图片原始比例并居中显示,而用android:background设置的会将图片拉伸直至铺满整个 ImageView。这里有一个疑问,为什么尺寸不一致的时候,Android 系统是采取居中显示,而不是其他的样式呢?这就是我们接下来要说明的属性了。特别注意以上说的都是尺寸比例,并非长和宽的值。
下面的列表总结了类和对象的关系和区别:对象是一个具体的实体,例如:特朗普是一个对象类是对多个具有相同属性的实体的抽象,例如:特朗普和成龙等实体具有 “姓名、年龄、工作” 等属性,通过归纳形成一个抽象概念——人,人是一个类对象与类的关系:对象属于某个类、对象不属于某个类,例如:特朗普是一个人,或者说特朗普属于人类
计算机语言分成解释型语言和编译型语言两种。我们下面来展开讲一下解释型语言和编译型语言两者的区别:在说两区别之前我们先来讲一下计算机怎么把代码翻译成计算机能看得懂的语言(翻译成机器码)。众所周知,计算机 CPU 的集成电路中,除了电容、电阻、电感就是晶体管了,每个晶体管相当于一个开关,理论上 CPU 只能存储识别两个标识符,那就是 0 和 1,所以说 CPU 识别的指令集只能由 0 和 1 组合。那么所有的计算机语言想要 CPU 能看得懂,必须翻译成 0/1 代码才行,这个由 0/1 组成的代码叫做机器码。但是机器码相对于人来说过于繁琐,所以就有人发明了高级语言、低级语言等等,这些语言的分级是根据它的语法是贴近人还是贴近机器来区分的,越贴近人它就越高级,越贴近机器它就越低级,但是最终想要 CPU 可以识别都需要翻译成机器码。典型的低级语言包括刚刚提到的机器码、汇编语言、c 等,高级语言包括 PHP、c#、JavaScript、Java、Python 等等。什么是编译型语言和解释性语言呢?刚刚我们提到翻译成机器码,这个翻译的过程就叫做编译或解释。编译型语言是指通过编译器翻译成完整的机器码,然后通过 CPU 去执行。而解释型语言是指通过一个虚拟机的方式一行行的翻译,翻译一行执行一行;还有一种方式是混合型,介于两者之间。常见的编译型语言包括 c++、c、rust等,解释型语言包括 JavaScript、PHP、HTML 等等,混合型包括 Python、Java等。
在 HBuilderX 编辑器中进行页面创建时,可以选择创建为 vue 页面还是 nvue页面。vue 页面与 nvue 页面虽然可以在同一个 uni-app 项目中共存,但是这两种页面的开发还是有区别的,我们进行项目开发的时候需要注意一下。
相似处:volatile 的内存语义和 synchronized 有相似之处,具体来说就是,当线程写入了 volatile 变量值时就等价于线程退出 synchronized 同步块(把写入工作内存的变量值同步到主内存),读取 volatile 变量值时就相当于进入 synchronized 同步块( 先清空本地内存变量值,再从主内存获取最新值)。区别:使用锁的方式可以解决共享变量内存可见性问题,但是使用锁太笨重,因为它会带来线程上下文的切换开销。具体区别如下:volatile 本质是在告诉 jvm 当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;synchronized 则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住;volatile 仅能使用在变量级别;synchronized 则可以使用在变量、方法、和类级别的;volatile 仅能实现变量的修改可见性,不能保证原子性;而 synchronized 则可以保证变量的修改可见性和原子性;volatile 不会造成线程的阻塞;synchronized 可能会造成线程的阻塞;volatile 标记的变量不会被编译器优化;synchronized 标记的变量可以被编译器优化
上面两个问题解决了,再看它们的区别其实就是显而易见的,主要从以下几个方面来分析它们之间的区别:var 声明的变量是全局作用域下的,会污染全局变量;let、const 可以和 {} 连用,产生块作用域不会污染全局;var 存在提升,我们能在声明之前使用。let、const 因为暂时性死区的原因,不能在声明前使用;var 在同一作用域下,可以重复声明变量;let、const 不能重复声明变量,否则会报错;let 和 const 的用法基本一致,但是 const 声明的变量不能再次赋值。
建议用法:CMD ["可执行命令", "参数1", "参数2"...]示例:CMD ["echo" "hello"]docker run 没有指定其他命令时,CMD 指令会在容器执行。Dockerfile 中 CMD 只能有一个,如果写了多个 CMD,则以最后一个为准。Tips:ENTRYPOINT 与 CMD 类似,但不会被 docker run 指定的命令覆盖。
首先给出进程和线程的基本定义:进程(Process) :是操作系统任务调度的基本单元, 目的是为了实现操作系统的并发。线程(Thread) :线程是进程的子任务,是进程中实际运行的任务,线程是程序执行的最小单元。然后分析两者之间的主要区别:(1)包含关系:一个线程肯定归属于一个进程,但是一个进程可以包含多个线程。(2)内存管理:操作系统会给进程分配独立的内存空间,但是一个进程下的多个线程共享内存空间。以 Java 编程为例,同一个 main 函数进程下的多个线程共享代码段(代码和常量),以及数据段(全局变量和静态变量),这些都是共享的内存空间,不过需要注意,每个线程会有独立的运行栈空间。(3)单元定义:从内存分配的角度就能看出,进程是资源分配的最小单元,线程是 CPU 执行的最小单元。(4)系统开销:创建进程和线程的系统开销是不同的,因为在创建和销毁进程时,操作系统需要分配和回收内存资源,创建线程不需要切换整体内存空间,所以创建进程的系统开销远大于创建线程;在切换进程时需要保存 CPU 运行的上下文,切换线程只需要切换 CPU 中少数寄存器的内容,所以切换进程的系统开销也远大于切换线程。(5)稳定性分析:因为不会共用内存空间,所以一个进程挂了对另外的进程影响很小,但是同一进程下的线程是共享内存的,所以一个线程挂了,会影响到其他线程。(6)通信:因为不同进程处于不同的内存空间,所以通信方式比较麻烦,具体方式将在之后的小节介绍。同一进程下的线程之间通信方式相对简单,因为共享内存,可以读写相同的内存空间。
上面我们简单介绍了 Flask 和 Django 这两个 Web 开发框架,下面,我给大家总结一下这两个框架的区别到底在哪里:其实两者最主要区别在于:Django 功能大而全,Flask 只包含基本的功能。Django 采用一站式解决的思路,Django 集成有模板、表单、路由、认证、基本的数据库管理等内建功能,开发者不用在选择应用的基础设施上花费大量时间。Django 就像是一个装潢好的房子(如下图所示),它提供了各种家具,直接入住即可。Flask 相对于 Django 而言是轻量级的 Web 框架。和 Django 不同,Flask 只提供了最核心的功能,轻巧、简洁,通过定制第三方扩展来实现具体功能。默认情况下,不包含数据库抽象、用户认证、表单验证、发送邮件等其它 Web 框架经常包含的功能。Flask 依赖用各种灵活的扩展(比如邮件 Flask Mail,用户认证 Flask Login,数据库 Flask SQLAlchemy)来给Web应用添加额外功能。Flask 就像是一个没有装潢的房子(如下图所示),用户需要自己选择各种家具才可以入住。
使用include或者extend引入的方法如果和被引入的模块/类中的方法重名,将不会覆盖被引入模块的方法,但是prepend会覆盖被引入模块的方法。下面是 include 的例子。实例:module Person def name "My name is Person" endendclass Member include Person def name "My name is Andrew" endendputs Member.new.name# ---- 输出结果 ----My name is Andrew而 prepend 则是这个样子。实例:module Person def name "My name is Person" endendclass Member prepend Person def name "My name is Andrew" endendputs Member.new.name# ---- 输出结果 ----My name is Person我们会发现,原本的 name 实例方法被覆盖了。
从手动检查更新的弹出框中我们可以看到这样的描述 “The latest stable release”,为什么要说是 stable release 呢?因为 Android Studio 存在不同的发布版本,各版本之间有什么区别呢?
JavaScript 在设计之初主要用来开发 Web 页面的交互、动画和表单验证等单一的功能,而且程序的体积很小,大多数都是独立执行的。随着前端的发展 JavaScript 承接的功能越来越多,Node 的出现让 JavaScript 可以作为一门后端开发语言,程序的复杂度瞬间提升,所以有必要提供一种将 JavaScript 程序拆分为可按需导入的单独模块的机制。Node 是 JavaScript 的先行者,它使用了 CommonJS 的规范来实现模块化的。在 ES6 没出来之前有很多模块化的实践,比较有名的有:CommonJS、AMD、CMD,每个规范都有自己独立的思考。随着 ES6 模块的发布,AMD 和 CMD 慢慢地淡出了我们的视野。现在主要常见的场景是 Node 端还采用 CommonJS 规范,这是历史原因。前端使用的是 ES6 module 规范,但是不能直接在前端使用,需要通过打包工具进行编译如:Webpack、Babel、Rollup 等。本文中我们将使用 Webpack 进行模块化编译工具,源代码放在 GitHub 上,仅供参考。
其实在 Kotlin 中还有一个非常类似于类型别名 (type lias) 的概念,叫做 Import As. 它允许你给一个类型、函数或者属性一个新的命名,然后你可以把它导入到一个文件中。例如:import android.support.v4.app.NotificationCompat.Builder as NotificationBuilder在这种情况下,我们从 NotificationCompat 导入了 Builder 类,但是在当前文件中,它将以名称 NotificationBuilder 的形式出现。你是否遇到过需要导入两个同名的类的情况?如果有,那么你可以想象一下 Import As 将会带来巨大的帮助,因为它意味着你不需要去限定这些类中某个类。例如,查看以下 Java 代码,我们可以将数据库模型中的 User 转换为 service 模型的 User。package com.example.app.service;import com.example.app.model.User;public class UserService { public User translateUser(com.example.app.database.User user) { return new User(user.getFirst() + " " + user.getLast()); }}由于此代码处理两个不同的类,但是这两个类都叫 User,因此我们无法将它们两者都同时导入。相反,我们只能将其中某个以类名 + 包名全称使用 User。利用 Kotlin 中的 Import As, 就不需要以全称类名的形式使用,仅仅只需要给它另一个命名,然后去导入它即可。package com.example.app.serviceimport com.example.app.model.Userimport com.example.app.database.User as DatabaseUserclass UserService { fun translateUser(user: DatabaseUser): User = User("${user.first} ${user.last}")}此时的你,或许想知道,类型别名 (type alias) 和 Import As 之间的区别?毕竟,您还可以用 typealias 消除 User 引用的冲突,如下所示:package com.example.app.serviceimport com.example.app.model.Usertypealias DatabaseUser = com.example.app.database.Userclass UserService { fun translateUser(user: DatabaseUser): User = User("${user.first} ${user.last}")}没错,事实上,除了元数据 (metadata) 之外,这两个版本的 UserService 都可以编译成相同的字节码!所以,问题来了,你怎么去选择你需要那一个?它们之间有什么不同?这里列举了一系列有关 typealias 和 import as 各自支持特性情况如下:目标对象 Typealias 别名 import asInterfaces and Classes (接口和类)YESNONullable Types (可空类型)YESNOGenerics with Type Params (泛型类型参数)YESNOFunction Types (函数类型)YESNOEnum (枚举类型)YESYESEnum Member (枚举成员)NOYESobject (对象表达式)YESYESobject Function (对象表达式函数)NOYESobject Properties (对象表达式属性)NOYES此外还需要注意的是:类型别名可以具有可见性修饰符,如 internal 和 private ,而它访问的范围是整个文件;如果您从已经自动导入的包中导入类,例如 kotlin.* 或 kotlin.collections* ,那么您必须通过该名称引用它。 例如,如果您要将 import kotlin.String 写为 RegularExpression ,则 String 的用法将引用 java.lang.String .
首先我们需要注意和明确的一点就是 MutableList<*> 和 MutableList<Any?> 是不一样的,MutableList<*> 表示包含某种特定类型的集合;而 MutableList<Any?> 则是包含任意类型的集合。特定类型集合只不过不太确定是哪种类型,任意类型表示包含了多种类型,区别在于特定集合类型一旦确定类型,该集合只能包含一种类型;而任意类型就可以包含多种类型了。
在 Python 中,集合与列表的区别如下:列表中的元素允许重复,集合中的元素不允许重复,示例如下:>>> x = {1, 1, 2, 3}>>> x{1, 2, 3}在以上程序中,元素 1 被重复了 2 次,在创建集合时,重复的元素被合并成一个。列表是有序的,提供了索引操作,集合是无序的,没有索引操作,示例如下>>> x = {1, 2, 3}>>> x[0]Traceback (most recent call last): File "<stdin>", line 1, in <module>TypeError: 'set' object does not support indexing在以上程序中,试图使用索引操作符访问集合的第 0 项元素。集合是无序的,不提供索引操作,运行时出错,提示: ‘set’ object does not support indexing。
Java 中的线程分为两类,分别为 daemon 线程(守护线程〉和 user 线程(用户线程)。在 JVM 启动时会调用 main 函数, main 函数所在的线程就是一个用户线程,其实在 JVM 内部同时还启动了好多守护线程,比如垃圾回收线程。守护线程定义:所谓守护线程,是指在程序运行的时候在后台提供一种通用服务的线程。比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。用户线程定义:某种意义上的主要用户线程,只要有用户线程未执行完毕,JVM 虚拟机不会退出。区别:在本质上,用户线程和守护线程并没有太大区别,唯一的区别就是当最后一个非守护线程结束时,JVM 会正常退出,而不管当前是否有守护线程,也就是说守护线程是否结束并不影响 JVM 的退出。言外之意,只要有一个用户线程还没结束, 正常情况下 JVM 就不会退出。
元组与列表很相似,都是有序的只读序列,两者有相同的方法和操作运算,它们的区别在于:列表创建后,可以进行修改元组创建后,不能进行修改修改列表的示例如下:>>> x = [1, 2, 3]>>> x[0] = 11>>> x[11, 2, 3]在第 1 行,创建了列表 x在第 2 行,修改列表 x 的第 0 项元素在第 3 行,显示修改后的列表在第 4 行,结果表示修改成功了修改元组的示例如下:>>> y = (1, 2, 3)>>> y[0] = 11Traceback (most recent call last): File "<stdin>", line 1, in <module>TypeError: 'tuple' object does not support item assignment>>> y(1, 2, 3)在第 1 行,创建了元组 y在第 2 行,修改元组 y 的第 0 项元素在第 3 行,提示出现 TypeError,因为 ‘tuple’ object does not support item assignment,所以无法修改在第 6 行,显示元组 y在第 7 行,结果表示元组 y 没有被修改
Android 模拟器除了需要满足 Android Studio 的基本系统要求之外,还需要满足下述其他要求:SDK Tools 26.1.1 或更高版本;64 位处理器;HAXM 6.2.1 或更高版本(建议使用 HAXM 7.2.0 或更高版本)。要在 Windows 和 Linux 上使用硬件加速,还需要满足以下其他要求:在 Windows 或 Linux 上搭载 Intel 处理器:Intel 处理器需要支持 Intel VT-x、Intel EM64T (Intel 64) 和 Execute Disable (XD) Bit 功能;在 Linux 上搭载 AMD 处理器:AMD 处理器需要支持 AMD 虚拟化 (AMD-V) 和 Supplemental Streaming SIMD Extensions 3 (SSSE3);在 Windows 上搭载 AMD 处理器:需要 Android Studio 3.2 或更高版本以及 2018 年 4 月发布的支持 Windows Hypervisor Platform (WHPX) 功能的 Windows 10 或更高版本。如需与 Android 8.1(API 级别 27)及更高版本的系统映像配合使用,连接的摄像头必须能够捕获 720p 的帧。
我们通过上述扩展函数的学习都知道,扩展函数在外部调用方式来看和类的成员函数是一致,但是两者却有着本质的区别。扩展函数和成员函数使用方式类似,可以直接访问被扩展类的方法和属性。(原理: 传入了一个扩展类的对象,内部实际上是用实例对象去访问扩展类的方法和属性);扩展函数不能打破扩展类的封装性,不能像成员函数一样直接访问内部私有函数和属性。(原理: 原理很简单,扩展函数访问实际是类的对象访问,由于类的对象实例不能访问内部私有函数和属性,自然扩展函数也就不能访问内部私有函数和属性了);扩展函数实际上是一个静态函数是处于类的外部,而成员函数则是类的内部函数;父类成员函数可以被子类重写,而扩展函数则不行。
CommonJS 和 AMD 的环境里都有一个 exports 变量,这个变量包含了一个模块的所有导出内容。CommonJS 和 AMD 的 exports 都可以被赋值为一个 对象, 这种情况下其作用就类似于 EcmaScript 2015 语法里的默认导出,即 export default 语法了。虽然作用相似,但是 export default 语法并不能兼容 CommonJS 和 AMD 的 exports。为了支持 CommonJS 和 AMD 的 exports, TypeScript 提供了 export = 语法。export = 语法定义一个模块的导出 对象。 这里的 对象 一词指的是类,接口,命名空间,函数或枚举。若使用 export = 导出一个模块,则必须使用 TypeScript 的特定语法 import module = require('module') 来导入此模块。export = 只能导出 对象export = 导出的模块只能用 import = require() 形式导入文件 ZipCodeValidator.ts:let numberRegexp = /^[0-9]+$/class ZipCodeValidator { isAcceptable(s: string) { return s.length === 5 && numberRegexp.test(s) }}export = ZipCodeValidator代码解释: 使用 export = 语法导出一个类对象。文件 Test.ts:import Zip = require('./ZipCodeValidator')// Some samples to trylet strings = ['Hello', '98052', '101']// Validators to uselet validator = new Zip()// Show whether each string passed each validatorstrings.forEach(s => { console.log(`'${ s }' - ${ validator.isAcceptable(s) ? 'matches' : 'does not match' }`)});代码解释: 通过 import = require() 形式导入。
很多同学很容易遇到以下问题,并且会想不通。实例:ch.pipeline().addLast(new InboundHandler1());ch.pipeline().addLast(new InboundHandler2());ch.pipeline().addLast(new OutboundHandler1());ch.pipeline().addLast(new OutboundHandler2());InboundHandler2 流转代码public class InboundHandler2 extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("inbound2>>>>>>>>>"); //流转到OutboundHandler2 ctx.channel().writeAndFlush("hello world"); }}执行结果:inbound1>>>>>>>>>inbound2>>>>>>>>>outbound2>>>>>>>>>outbound1>>>>>>>>>修改 InboundHandler2 流转代码public class InboundHandler2 extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("inbound2>>>>>>>>>"); //【注意,调整这里了】 ctx.writeAndFlush("hello world"); }}执行结果inbound1>>>>>>>>>inbound2>>>>>>>>>思考:为什么这里使用 ctx.writeAndFlush 就流程不下去了呢?ctx.writeAndFlush(); 最终源码private AbstractChannelHandlerContext findContextOutbound() { AbstractChannelHandlerContext ctx = this; do { ctx = ctx.prev; } while(!ctx.outbound); return ctx;}通过源码,我们发现它是从当前 InboundHandler 开始往前执行。ctx.channel().writeAndFlush(); 最终源码public final ChannelFuture writeAndFlush(Object msg) { return this.tail.writeAndFlush(msg);}通过源码,我们发现它是从链表的最后一个节点开始往前面执行。总结,如果是 OutboundHandler 放在 InboundHandler 之后,使用不同的 writeAndFlush 则得到的结果不一样。
联系 1:都有一组类似的方法.Object 对象监视器: Object.wait()、Object.wait(long timeout)、Object.notify()、Object.notifyAll()。Condition 对象: Condition.await()、Condition.awaitNanos(long nanosTimeout)、Condition.signal()、Condition.signalAll()。联系 2:都需要和锁进行关联。Object 对象监视器: 需要进入 synchronized 语句块(进入对象监视器)才能调用对象监视器的方法。Condition 对象: 需要和一个 Lock 绑定。区别:Condition 拓展的语义方法,如 awaitUninterruptibly () 等待时忽略中断方法;在使用方法时,Object 对象监视器是进入 synchronized 语句块(进入对象监视器)后调用 Object.wait ()。而 Condition 对象需要和一个 Lock 绑定,并显示的调用 lock () 获取锁,然后调用 Condition.await ();从等待队列数量看,Object 对象监视器是 1 个。而 Condition 对象是多个。可以通过多次调用 lock.newCondition () 返回多个等待队列。
binlog是逻辑日志,记录某个语句的基本逻辑,即SQL语句;redo log是物理日志,记录在某个数据页所做的修改;binlog是在MySQL的Server层实现,所有的存储引擎都可以使用binlog这个日志模块;redo log是InnoDB存储引擎特有的日志模块;binlog是追加写,在写满或重启之后,会生成新的binlog文件,之前的日志不会进行覆盖;redo log是循环写,空间大小是固定的;binlog 是在事务最终提交前写入的;redo log是在事务执行过程不断的写入;binlog可以应用于数据归档、主从搭建等场景;redo log作为异常宕机或者介质故障后的数据恢复使用;
从内存角度出发,值类型放在内存栈中,引用类型则放在内存堆中。引用类型的数据长度是不固定的,如对象所占用的空间很大一部分由属性值决定,而属性值又可以是任意类型。另外最大的区别就如分类名一样,引用类型的数据本身是指向内存上的一块地址,操作的时候对地址上的值进行操作。而值类型直接操作值,不论是复制或是修改都是直接产生一个新的值。var obj1 = { name: '小明',};var obj2 = obj1;obj2.name = '小红';console.log(obj1.name); // 输出:小红var val1 = 1;var val2 = val1;val2 = 2;console.log(val1); // 输出:1通过上面的例子就可以看出引用类型和值类型在 JavaScript 程序中的区别。引用类型在进行复制的时候,其实就是多了一个引用,操作的值是同一个。而值类型进行复制后,则是一个新的值。
接口的方法默认是 public ,所有方法在接口中不能有实现(Java 8 开始接口方法可以有默认实现),而抽象类可以有非抽象的方法;接口中除了 static 、final 变量,不能有其他变量,而抽象类可以;一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过 extends 关键字扩展多个接口;接口方法默认修饰符是 public ,抽象方法可以有 public 、protected 和 default 这些修饰符(抽象方法就是为了被重写所以不能使用 private 关键字修饰!);从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。
事务是一组原子性的SQL查询,事务内的SQL语句,要么全部执行成功,要么全部执行失败。本节重点介绍事务的ACID和隔离级别。
主分区主要是用来启动操作系统的,它主要放的是操作系统的启动或引导程序,/boot分区最好放在主分区上。扩展分区不能使用的,它只是做为逻辑分区的容器存在的;我们真正存放数据的是主分区和逻辑分区,大量数据都放在逻辑分区中如果你用的是 GPT 的分区方式,那么它没有限制主分区个数Tips:从MBR转到GPT分区或者说从GPT转到MBR会导致数据全部丢失。
应用生命周期函数比较好区分,是用来监听整个应用的。有一个很好的判断方法就是,写在 App.vue 文件中生命周期函数就是应用生命周期函数。页面生命周期函数和组件生命周期函数都是对一个页面中状态的监听,比较容易混淆。简单来说,应用生命周期函数仅在 page 页面有效,在单独封装的组件中无效。但是组件生命周期函数在 page 页面和单独封装的组件中都有效。
类型别名看起来和接口非常类似,区别之处在于:接口可以实现 extends 和 implements,类型别名不行。类型别名并不会创建新类型,是对原有类型的引用,而接口会定义一个新类型。接口只能用于定义对象类型,而类型别名的声明方式除了对象之外还可以定义交叉、联合、原始类型等。类型别名是最初 TypeScript 做类型约束的主要形式,后来引入接口之后,TypeScript 推荐我们尽可能的使用接口来规范我们的代码。