1、单例设计模式
饿汉式:类加载时创建,不存在线程安全问题
直接初始化
枚举
静态代码块(适合复杂实例化)
懒汉式:类使用时创建,存在线程安全问题
getInstance方式(线程不安全)
加锁外层加if判断(线程安全)
静态内部类(线程安全且简洁)(静态内部类在使用时创建,不随着外部类加载而加载)
2、类初始化和实例(对象)初始化
main方法所在的类需要初始化
类初始化(加载类)(只初始化类,不初始化对象):执行clinit()方法 (子类初始化需先初始化父类)
clinit方法由 静态类变量显示赋值代码 和 静态代码块 组成 , 从上到下顺序执行 , 只执行一次
实例的初始化(创建对象):就是执行init()方法每次创建实例对象,都要调用对应构造器(执行init方法)
init()方法可能重载有多个,有几个构造器就有几个init方法
init方法由 非静态实例变量显示赋值代码 和 非静态代码块(从上到下顺序执行) 、 对应构造器代码(最后执行) 组成
init方法的首行一定是super()或super(实参列表),即对应父类的init方法
3、Spring中bean的作用域
bean的作用域:默认值singleton 单例
可以通过scope属性来指定bean的作用域
singleton :默认值。当IOC容器一创建就会创建bean的实例,而且是单例的,每次得到的都是同一个
propertype :原型的。IOC容器创建时不实例化该bean,当每次调用getBean方法时再实例化该bean,而且每次调用都返回一个新的实例
-request :每次请求实例化一个bean
-session :再一次会话中共享一个bean
4、数据库
4.1 数据库事务的4大特性:(ACID)
- 原子性 回滚 要么全部成功,要么全部失败
- 一致性 转账 500 500 -> 600 400
- 隔离性 多个线程操作数据库,是相互隔离的,对T1线程,要么T2线程已经结束,要么T2线程还没开始 可设置隔离级别
- 持久性 事务一旦提交,就会永久性改变数据库中的数据,即使数据库系统故障也不会丢失数据
4.2 事务传播行为(7):
最常用的两种
REQUIRED:如果有事务在运行,当前的方法就在这个事务内运行,否则,就启动一个新的事务,并在自己的事务内运行。
REQUIRES_NEW:当前的方法必须启动新事物,并在它自己的事物内运行,如果有事务正在运行,应该将它挂起。
事务并发执行的问题:
- 脏读:T1修改AGE的值从20到30,T2读取到更新的值30,T1事务内其他地方放生异常回滚AGE变为20,T2读取到的30是无效值,由于其他事务的回滚导致另外的事务读取到了错误的值。
- 不可重复读:T1读取AGE值为20,T2将AGE修改为30,T1再次读取AGE的值为30,一个事务内前后读取数据不一致
- 幻读:T1读取STU表中一部分数据,T2向STU表中插入了新的行,T1再次读取STU表时,多读出了一些行,一个事务内前后读取数据不一致。
由此引出事务的隔离级别
4.3 事务隔离级别(4):两个事务并发执行,数据库隔离各个事务的能力,使之不会相互影响
(一个事务与其他事务隔离的程度称为隔离级别)
- 1)读未提交
- 2)读已提交 最常用
- 3)可重复读
- 4)串行化
isolation=Isolation.REPEATABLE_READ 可重复读 MYSQL默认隔离级别
Isolation.READ_COMMITTED 读已提交 ORACLE默认隔离级别,最常用
事务配置:事务的方法上添加注解
@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.READ_COMMITTED)
5、GET与POST:
1、POST乱码原因:request请求在request.getParameter(“参数名”)得到参数时,会查码表,如果没有设置,查的是Iso8859-1(老外写的)的码表,而我们表单页面的码表是UTF-8(meta设置)。提交的 中文 数据 使用UTF-8码表解码,然后使用ISO8859-1码表编码,所以会得到乱码数据
2、SpringMVC中如何解决Post请求中文乱码 :在web.xml中配置
<!-- 配置springmvc自带的Post请求乱码 -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
相当于在servlet中 request.setCharacterEncoding(“UTF-8”)(原生写法)
3、get乱码原因:get请求使用的是HTTP协议的另一种发送数据方法,所以解码设置不在request中设置,而在tomcat服务器的server.xml文件的Connector 的 URIEncoding属性设置
4、解决get请求乱码 :在server.xml中添加 URIEncoding=”UTF-8” 大小写无所谓 或者在servlet中 username = new String(username.getBytes(“iso8859-1”),”UTF-8”) (原生写法)
GET和POST两种基本请求方法的区别:原文链接
https://www.cnblogs.com/logsharing/p/8448446.html
5、GET请求与POST请求的区别:最直观的区别就是GET把参数包含在URL中,POST通过request body传递参数。由此引发了下面一系列区别
GET在浏览器回退时是无害的,而POST会再次提交请求。
GET产生的URL地址可以被Bookmark,而POST不可以。
GET请求会被浏览器主动cache,而POST不会,除非手动设置。
GET请求只能进行url编码,而POST支持多种编码方式。
GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
GET请求在URL中传送的参数是有长度限制的,而POST么有。
对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
GET参数通过URL传递,POST放在Request body中。
(本标准答案参考自w3schools)
但是:
6、GET和POST是HTTP协议中两种发送请求的方法。HTTP是基于TCP/IP 关于数据如何在万维网中通信的协议。GET/POST都是TCP链接,底层并无差别。但是由于HTTP的规定和浏览器/服务器的限制,导致他们在应用过程中体现出一些不同。所以才有以上的区别。
7、GET和POST还有一个重大区别,简单的说:
GET产生一个TCP数据包;POST产生两个TCP数据包。(并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次)
对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)
也就是说,GET只需要汽车跑一趟就把货送到了,而POST得跑两趟,第一趟,先去和服务器打个招呼“嗨,我等下要送一批货来,你们打开门迎接我”,然后再回头把货送过去。
6、MyBatis中当实体类中的属性名(lastName)和表中的字段名(last_name)不一样,怎么办?
- 1、Mapper.xml配置文件中写sql语句时起别名 select id , last_name lastName , email from user
- 2、MyBatis配置文件(mybatis-config.xml)中开启驼峰命名规则,可以将数据库中的下划线映射为驼峰命名
例如:last_name 映射为 lastName<setting name = "mapUnderscoreToCamelCase" value = "true" />
- 3、Mapper映射文件中使用resultMap来自定义映射规则
7、Git相关
Git:世界上最先进的分布式版本控制工具
7.1 分支相关命令
创建分支:
- git branch <分支名>
- git branch -v 查看分支
切换分支
- git checkout <分支名>
- git checkout -b <分支名> 一步完成:创建分支并切换到此分支
合并分支
- 先切换到主干分支 git checkout master
- git merge <分支名>
删除分支
- 先切换到主干 git checkout master
- git branch -D <分支名> 大写的D
7.2 实际应用
8、HTTP协议的8种请求类型介绍
参考链接:https://www.cnblogs.com/liangxiaofeng/p/5798607.html
HTTP协议中共定义了八种方法或者叫“动作”来表明对Request-URI指定的资源的不同操作方式,具体介绍如下:
- OPTIONS:返回服务器针对特定资源所支持的HTTP请求方法。也可以利用向Web服务器发送’*’的请求来测试服务器的功能性。
- HEAD:向服务器索要与GET请求相一致的响应,只不过响应体将不会被返回。这一方法可以在不必传输整个响应内容的情况下,就可以获取包含在响应消息头中的元信息。
- GET:向特定的资源发出请求。
- POST:向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的创建和/或已有资源的修改。
- PUT:向指定资源位置上传其最新内容。
- DELETE:请求服务器删除Request-URI所标识的资源。
- TRACE:回显服务器收到的请求,主要用于测试或诊断。
- CONNECT:HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
9、SSL四次握手,TCP三次握手
SSL/TLS协议四次握手
参考链接:https://blog.csdn.net/odyyy/article/details/80256129
TCP三次握手,四次挥手:
参考链接:https://blog.csdn.net/a1065712890/article/details/80864516
10、HTTP有关缓存的几个字段
Expires、Cache-Control、Last-Modified、 ETag是RFC 2616(HTTP/1.1)协议中和网页缓存相关的几个字段。前两个用来控制缓存的失效日期,后两个用来验证网页的有效性。
参考链接:
https://blog.csdn.net/lifeibo/article/details/5979572
11、基于TCP/IP协议的应用层协议有哪些
1、SMTP:简单邮件传输协议
2、Telnet:远程登录协议
3、SNMP:简单网络管理协议
4、FTP:文件传输协议
5、LPD:行式打印机守护进程
6、TFTP:简单文件传输协议
7、NFS:网络文件系统协议
12、Mybatis中#{}占位符 和 ${}拼接符 的区别
#{}相当于预编译preparedstatement,${}是直接使用里面的值进行拼接
https://blog.csdn.net/aphysia/article/details/80814626
13、gc垃圾回收器
https://www.cnblogs.com/firstdream/p/5763646.html
14、java中的volatile关键字
volatile修饰的变量:用以标识变量的值随时可能被别的线程修改,使用volatile修饰的变量的值发生改变时,会强制立即将改变后的值写入主存,主存中的值更新会使缓存中的值失效(非volatile修饰的变量不具有这样的属性,并发编程中,线程A更新了这个值,线程B读取到这个变量的值可能并不是线程A修改后的值。)
volatile具有可见性,有序性。不具备原子性。volatile变量是一种比sychronized关键字更轻量级的同步机制。
volatile 性能:
volatile 的读性能消耗与普通变量几乎相同,但是写操作稍慢,因为它需要在本地代码中插入许多内存屏障指令来保证处理器不发生乱序执行
当一个变量定义为 volatile 之后,将具备两种特性:
1.保证此变量对所有的线程的可见性。
2.禁止指令重排序优化。有volatile修饰的变量,赋值后多执行了一个“load addl $0x0, (%esp)”操作,这个操作相当于一个内存屏障(指令重排序时不能把后面的指令重排序到内存屏障之前的位置),只有一个CPU访问内存时,并不需要内存屏障;(什么是指令重排序:是指CPU采用了允许将多条指令不按程序规定的顺序分开发送给各相应电路单元处理)
15、Hash表和Hash算法,HashMap
什么是哈希算法?
Hash,一般翻译做“散列”,也有直接音译为“哈希”的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。(就是通过算法,将任意长度的输入转化为固定长度的输出。当然可能会有不同的输入经过算法运算后得到相同的输出。这叫做冲突)
这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。
Hash算法特别的地方在于它是一种单向算法,用户可以通过hash算法对目标信息生成一段特定长度的唯一hash值,却不能通过这个hash值重新获得目标信息。因此Hash算法常用在不可还原的密码存储、信息完整性校验等。
常见的Hash算法有MD2、MD4、MD5、HAVAL、SHA
常见的Hash算法有加法Hash、位运算Hash、乘法Hash、除法Hash等
“将固定长度的输入转化为相同长度的输出”,下面是一个简单的哈希算法:
int Hash(int num)
{
return (num*3)%7;
}
没错就是这么简单,将任意长度的数字输入转化为[0,7)之间的固定输出
String类的hashCode算法
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
使用 int 算法,这里 s[i] 是字符串的第 i 个字符,n 是字符串的长度,^ 表示求幂。(空字符串的哈希值为 0。)
哈希表
根据设定的哈希函数H(key)和所选中的处理冲突的方法,将一组关键字映射到一个有限的、地址连续的地址集(区间)上并以关键字在地址集中的“象”作为相应记录在表中的存储位置,这种表被称为哈希表。
数组的特点是:寻址容易,插入和删除困难;而链表的特点是:寻址困难,插入和删除容易。那么我们能不能综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?答案是肯定的,这就是的哈希表
HashMap
HashMap是基于Hashing(哈希)的原理,我们使用put(key,value)储存数据到HashMap中,使用get(key)从HashMap中得到此key对应的value。
HashMap底层基于Hash表(数组+链表)实现的。
因为哈希算法的特点,固定长度的输入可以转化为特定长度的输出,这样必定会有不同的输入转化为同样的输出,所以相当于是一种映射关系:
哈希算法映射关系:
解释:
这种映射关系就像将一堆豆子一棒子打散到一个充满格子的框里,将这些豆子散列在固定的空间。
我们要用到数组的查询快速的优点,当然不能像真正的数组那样一个萝卜一个坑,所以我们使用“几个萝卜共用一个坑”这样的散列方法,而如果我们真的这样几个萝卜一个坑的话根本存不了,因为一个数组的位置只能放一个元素,加之另一个需求我们又想要增删快速,所以我们选择在数组的每个元素后面接一串链表,数组中存放链表首地址即可,这样同一个“坑”中的“萝卜”(元素)就能一一存放在数组中固定位置后面的链表节点中了。
当我们使用put(key,value)方法储存数据时,先对键key调用hashCode()方法,得到此key经过哈希算法运算后的哈希码值,这个哈希码值将作为数组中位置标识,可以理解为数组下标。
情况1: 如果算出的位置目前没有任何元素存储,那么该元素可以直接添加到哈希表中。
情况2:如果算出的位置目前已经存在其他的元素,那么还会调用该key的equals方法与这个位置上的元素的key进行比较,如果equals方法返回的是false,那么表示两个元素的key是不相同的,新来的元素允许被存储,则将新来的元素连接到链表末端;如果equals方法返回的是true,则表明两个key相同,那么此新来的元素被视为重复元素,则,此位置上的元素的key不变,value使用新来的元素的value。
验证:
package test;
import java.util.HashMap;
class User{
int id;
String name;
public User(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
@Override
public int hashCode() {
return this.id;
}
@Override
public boolean equals(Object obj) {
User u = (User)obj;
return this.id == u.id;
}
}
public class test {
public static void main(String[] args) {
HashMap<User,Integer> map = new HashMap();
map.put(new User(111,"aaa"),123);
map.put(new User(222,"bbb"),456);
map.put(new User(333,"ccc"),789);
map.put(new User(222,"ooo"),0);
System.out.println(map);
//{User{id=333, name='ccc'}=789, User{id=222, name='bbb'}=0, User{id=111, name='aaa'}=123}
//输出的是 222,bbb 0 因为出现重复的key(key.hashCode相同key.equals也相同)时,
// key使用原来的,value使用新来的
}
}
疑惑点:
(为什么调用元素的key的hashCode方法,得到的哈希码值相同时还要调用key的equals方法再判断一次是
否相同呢?
因为不同输入经过哈希算法运算可能得到相同的哈希码值,“冲突”,再调用一次equals方法就
是判断两个key是否真的相同,如果真的相同,那么key不变,value使用新来的,如果不同就接着和下一
个节点比较,到最后都一直没有相同的元素就往链表末端
添加。
综上所述HashMap对重复元素的处理方法是:key不变,覆盖value)
当我们使用get(key)方法得到数组数据时,先对键key调用hashCode()方法,得到此key经过运算后的哈希码值,然后用此标识到数组中的某个位置查找,当此位置后面的链表中某个节点的元素的key和此key相同时,则返回此value。若不相同则顺着此链表通过pNext指针比对下一个key和此key是否相等。
注:图中并没有画出key的hashCode相同,equals也相同时,value覆盖的情况
我手写了一个简单的哈希表(也可以叫做HashMap),不过我没有储存(key,value)数据,为追求简单只是储存了简单的int类型数据(单key,没有value。为了突出HashMap的原理和数据结构,等有空会再把(key,value)补上),详情:https://github.com/hanhanhanxu/MyHashTable (使用C++编写)
这个是我自己写的java语言实现的HashMap,使用的数据结构也是数组+链表,这里面链表实现方式很特别,而且插入时是头插法,目前实现了put,get,toString方法:https://github.com/hanhanhanxu/MyHashMap
16、JSP九大隐式对象和四大域
九大隐式对象
1、request 客户端请求,此请求会包含来自GET/POST请求的参数通过它才能了解到客户的需求,然后做出响应。
2、response 响应客户请求的有关信息。
3、session 客户端与服务器的一次会话,从客户端连到服务器的一个WebApplication开始,直到客户端与服务器断开连接为止。
4、application 它实现了用户间数据的共享,可存放全局变量。它开始于服务器的启动,直到服务器的关闭。
5、page JSP网页本身,page对象是当前页面转换后的Servlet类的实例。Object page = this;
6、pageContext JSP页面上下文,它提供了对JSP页面内所有对象及名字空间的访问。(pageContext对象是JSP技术中最重要的一个对象,它代表JSP的运行环境。这个对象不仅封装了对其他8大隐式对象的引用,它自身还是一个域对象,可以用来保存数据)
7、out 它是JspWriter类的实例,是向客户端输出内容常用的对象。
8、config 它是在一个Servlet初始化时,JSP引擎向它传递信息用的。
9、exception 它是一个例外对象,当一个页面在运行过程中发生了例外,就产生这个对象。
通过pageContext获得其他对象
四大域
pageContext(当前页面;page域)、
request(当前请求,可转发;request域)、
session(当前会话;session域)、
servletcontext(当前应用;application域)
17、java中的基本数据类型
计算机中最小的数据单位是位(bit,不可再分割) 8位 = 1个字节 8bit = 1Byte(B) 1024Byte = 1kb
计算机中储存数据最小的单位是字节(最基本单位,不可能一位一位的储存数据,8位构成一个字节,用字节储存数据,java中byte类型数据占1个字节,int类型数据占4个字节)
8中基本数据类型
数据类型 默认值 大小(二进制位数) 范围
byte 0 8(1字节) -128~127
short 0 16(2字节)
int 0 32(4字节)
long 0 64(8字节)
float 0.0d 32
double 0.0d 64
char \u0000 16
boolean false 1 ? 1bit还是1Byte
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Charset
boolean Boolean
装箱和拆箱
把基本数据类型转换为对应的包装类型
把包装类型转换为基本数据类型
Integer i = 1;
int j = i;
实际上是
Integer i = Integer.valueOf(1); 自动装箱 valueOf底层还是new Integer(1)
int j = i.intValue(); 自动拆箱
Q:为什么有了基本数据类型之后还要有包装类型
A:Java是一个面向对象的语言,而基本的数据类型,不具备面向对象的特性。
缓存:对象缓存,Integer i = 1;Integer j = 1; i==j 返回true ,因为1已经缓存了(valueOf),用的是用一个
18、java1.8新特性
- Lambda表达式
- 函数式接口
- *方法引用和构造器调用
- Stream API
- 接口中的默认方法和静态方法
- 新时间日期API
19、java V1.9-1.10新特性
- 模块系统
- 默认G1回收器
- 接口私有方法
- 局部变量推断
- Graal编译器
19、java V1.11 新特性 以后长期支持版本
- ZGC垃圾回收器
- 字符串API增强
- 内置HTTP Client
20、一次完整的http请求过程是怎样的
DNS解析,TCP建连,HTTP请求,HTTP相应
21、谈谈你对Spring的理解
简要:
Spring是免费开源的java框架,轻量级,非侵入式,一站式,Spring框架用来管理你的业务对象。
- 轻量级 不超过1M的jar文件
- 非侵入式 开销特别小
- 一站式 框架非常全面,整合度高,学了这个就不用学关于此部分的其他内容。
具体:
Spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器框架。两大核心IOC和AOP可单独应用于任何应用。
Spring中注入的bean是单例的吗?
Spring配置的bean默认为是单例的。配置bean属性scope=”prototype”(或者注解@Scope(“prototype”))则得到的bean为非单例。
scope叫做作用域,将bean的作用域由singleton变为prototype,是解决Spring单例bean线程不安全最浅显的方法
Spring的AOP的应用
事务管理,日志处理,安全验证
注解装配在Spring中默认是关闭的,所以需要在Spring的核心配置文件中配置一下,才能使用基于注解的装配模型:
<context:annotation-config />
以下三个配置了上一个,就不用配置下一个了
1:<context:component-scan base-package="pack.pack"/>
2:<context:annotation-config/>
3:@Autowired
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor "/>
@Required
<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>
SSM
使用SpringMVC作为系统的整体架构,负责MVC分离,控制业务跳转,利用Mybatis框架对持久层提供支持,Spring做管理,管理SpringMVC和Mybatis。
22、字符集和字符编码以及java中各阶段的编码方式
字符集和字符编码:
https://www.cnblogs.com/defias/p/3436517.html
https://www.cnblogs.com/xdyixia/p/9114145.html
字符集:一张表,表中储存了所需用到的所有单个字符,如a,b,c,d,1,2,3… 我 中 韩 1 2 3 a b c。常见字符集有ASCII字符集,GBXXXX系列字符集(GB2312,GBK),BIG5字符集等等
每个国家都可以有自己国家的一套字符集,只储存自己国家的字符,而互联网是全世界的,互通全世界时,会出现字符缺少的问题(我 这个字符在ASCII字符集中不存在),所以为了统一,创造出了Unicode字符集
Unicode(统一码、万国码、单一码、标准万国码)是业界的一种标准,它可以使电脑得以体现世界上数十种文字的系统。
字符编码:定义一套规则,将a如何编码为数字代码,如何将数字代码解码为a。计算机不能储存a,所以必须将字符集中的各个字符转换为计算机可以接受的数字系统的数,例如101011这样的数字代码,然后进行储存。
常见的字符编码方案:UTF-32/ UTF-16/ UTF-8等
通常特定的字符集采用特定的编码方式(即一种字符集对应一种字符编码(例如:ASCII、IOS-8859-1、GB2312、GBK,都是即表示了字符集又表示了对应的字符编码),但Unicode不是,它采用现代的模型)
java语言使用Unicode字符集
编码:
Java源码文件,*.java,可以是任意字符编码,如GBK,UTF-8
Class文件,*.class,采用的是一种改进的UTF-8编码(Modified UTF-8)
JVM,内存中使用UTF-16编码
Java编译器需要正确的读取源码,消除编码差异,然后编译成UTF-8编码的Class文件。比如javac,默认情况下它会取操作系统的编码,可以使用参数-encoding指定源码文件的字符编码。JVM加载Class文件,把其中的字符或字符串转成UTF-16编码序列。
E:\0面试>javac demo.java -encoding GBK
E:\0面试>java demo
111111
23、位bit与字节Byte以及java中字符串的大小
比特 二进制中的一位,信息的最小单位
字节 通常用作计算机信息计量单位,不分数据类型
计算机存储信息的最小单位是位(bit) 8位 = 1个字节 8bit = 1Byte(B) 1024Byte = 1kb
计算机中存储容量的最小单位是字节 (存储器中所包含存储单元的数量称为存储容量,其计量基本单位是字节)
java中怎样查看字符串的字节大小?
答:字符串的字节大小可查,但是结果不固定。因为字符串是经过编码后得到的,如果想要看某字符串占多少字节,要先对字符串进行解码,而解码又可以指定解码的字符集,使用不同字符集进行解码,得到的字节大小不同。
String.length()返回的是此字符串(字符序列)的长度,并不是此字符串的字节大小。如果字符串是中文,对于不同字符编码,中文长度一样(我:1 我是:2),将中文通过响应编码解码后得到的字节大小不一样
ASCII编码 1个字符占1个字节
ISO-8859-1编码 1个中文占1个字节
GBK编码 1个中文占2个字节
Unicode编码 1个中文或英文都占2个字节
UTF-8编码 1个中文占3个字节
String s2 = "我是";
System.out.println(s2.length());//1 长度≠大小
System.out.println("--------------------");
//getBytes:使用给定的 charset字符集 将此 String 编码到 byte 序列,并将结果存储到新的 byte 数组。
System.out.println(s2.getBytes("ISO-8859-1").length);//1个中文占一个字节
System.out.println(s2.getBytes("GBK").length);//1个中文占两个字节
System.out.println(s2.getBytes("UTF-8").length);//1个中文占三个字节
System.out.println(s2.getBytes("Unicode").length);//我 4 我是 6 。说明前面有两个标识,表明是字符串
System.out.println("--------------------");
System.out.println(s2.getBytes("ISO-8859-1"));//1个中文占一个字节
System.out.println(s2.getBytes("GBK"));//1个中文占两个字节
System.out.println(s2.getBytes("UTF-8"));//1个中文占三个字节
System.out.println(s2.getBytes("Unicode"));//1个中文/英文占四个字节
System.out.println("--------------------");
byte[] unicodes = s2.getBytes("Unicode");
for (byte by:unicodes){
System.out.println(by);
}
//如果使用Uniocode字符集,则得到的byte序列中,前两位固定字节位-2 -1,可能是标识这是字符串
24、MySQL查询性能集索引解决方案
查询性能分析
explain select * from test_simpleindex where name = ‘aaa’ ;
查询性能:
type:表示MySQL在表中找到所需行的方式,或者叫访问类型,常见类型如下(从左到右,性能由差到好)
ALL index range ref eq_ref const,system NULL
ALL: 全表扫描
index: 索引全扫描
range:索引范围扫描
ref:使用非唯一索引扫描
eq_ref:使用唯一索引扫描
const,system:单表中最多只有一个匹配行
效率:
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
优化:添加查询条件
possible_keys
指出MySQL能使用哪个索引在该表中找到行。如果是空的,没有相关的索引。这时要提高性能,可通过检验WHERE子句,看是否引用某些字段,或者检查字段不是适合索引。
这里id是一个自增字段,且为主键,insert into添加数据时,id从1自增,所以我们使用id!=0作为条件,这样能大大提升查询效率。
count(*)=count(1)>count(primary key)>count(column)
结果来自:
https://dwz.cn/9QiePaZP
利用show profiles;查看sql执行时间的长短:https://blog.csdn.net/qq_37469931/article/details/83415150
show variables like "%pro%";
set profiling = 1; on开启 off关闭
select count(*) from test_data;
show profiles;
https://blog.csdn.net/sj349781478/article/details/78224713
如果要查询count所有记录数的话,
select count(*) from tablename
是最好的
当查询记录时,尽量使用子查询,或id限制查询进行优化,
优化:建立索引
为name字段添加索引
create index index_name on test_simpleindex (name);
后,查询性能分析
type变为了ref:非唯一索引扫描。
另外需要注意的是:
主键自动建立索引,且查询性能为type = const,最好的。
25、MySQL索引
-索引
简单索引:
create table test_simpleindex (id int(10) unsigned auto_increment , name varchar(32) not null , user char(12) , primary key(id));
insert into test_simpleindex (name,user) values ('aaa','111aaa');
insert into test_simpleindex (name,user) values ('bbb','222bbb');
insert into test_simpleindex (name,user) values ('ccc','333ccc');
explain select * from test_simpleindex where id = 2 ;
type = const
create index index_name on test_simpleindex (name);
explain select * from test_simpleindex where name = 'aaa' ;
type = ref
drop index index_id on test_simpleindex ;
主键自动创建索引,且删除不掉,type=const。
组合索引:
create table test_compositeindex (id int unsigned auto_increment , name varchar(10) not null , user varchar(10) , primary key(id));
insert into test_compositeindex (name,user) values ('123qwe','asdf234');
insert into test_compositeindex (name,user) values ('aaasdf','bbbasd');
insert into test_compositeindex (name,user) values ('sdbbbdf','acccasfd');
insert into test_compositeindex (name,user) values ('eee','aaa');
explain select * from test_compositeindex ;
type = ALL
explain select name,user from test_compositeindex ;
type = ALL
create index index_name_user on test_compositeindex (name,user);
explain select * from test_compositeindex ;
type = index
explain select name,user from test_compositeindex ;
type = index
全文索引
create table test_fullindex (id int unsigned auto_increment, title varchar(32) , content text , primary key (id)) engine = MyISAM;
insert into test_fullindex (title,content) values ('qweaaazxc','ertyaaazxc');
insert into test_fullindex (title,content) values ('aaazsdf','wertaaazxcb');
insert into test_fullindex (title,content) values ('adafdaaa','asdffasdaaa');
insert into test_fullindex (title,content) values ('aaa','aaa');
insert into test_fullindex (title,content) values ('我是我我我','是我我我是');
insert into test_fullindex (title,content) values ('我问问问','问问问我');
insert into test_fullindex (title,content) values ('问问问','问问问');
explain select * from test_fullindex where title like '%aaa%' or content like '%aaa%';
type = ALL
模糊查询只匹配连续的内容:
mysql> select * from test_fullindex where title like '%我我我%' or content like '%我我我%';
+----+------------+------------+
| id | title | content |
+----+------------+------------+
| 5 | 我是我我我 | 是我我我是 |
+----+------------+------------+
1 row in set (0.00 sec)
mysql> select * from test_fullindex where title like '%aaa%' or content like '%aaa%';
+----+-----------+-------------+
| id | title | content |
+----+-----------+-------------+
| 1 | qweaaazxc | ertyaaazxc |
| 2 | aaazsdf | wertaaazxcb |
| 3 | adafdaaa | asdffasdaaa |
| 4 | aaa | aaa |
+----+-----------+-------------+
4 rows in set (0.01 sec)
为title和content字段添加全文索引
alter table test_textindex add fulltext index index_full_title_content (title,content);
如果显示不支持全文索引,则修改表的引擎,因为InnoDB不支持全文索引。
alter table test_textindex engine = MyISAM ;
MySQL自带的全文索引只能对英文进行全文检索,目前无法对中文进行全文检索
SELECT * FROM test_fullindex WHERE MATCH(title, content) AGAINST('aaaz');
查不出东西
mysql> explain SELECT * FROM test_fullindex WHERE MATCH(title, content) AGAINST('adafdaaa');
+----+-------------+----------------+----------+--------------------------+--------------------------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------------+----------+--------------------------+--------------------------+---------+------+------+-------------+
| 1 | SIMPLE | test_fullindex | fulltext | index_full_title_content | index_full_title_content | 0 | | 1 | Using where |
+----+-------------+----------------+----------+--------------------------+--------------------------+---------+------+------+-------------+
1 row in set (0.00 sec)
type = fulltext
26、字段属性数字的含义:
int(N)、tinyint(N)
N代表了显示长度,与可储存int值的大小无关,int(3)与int(10)与int所储存的数值大小范围都是-2147483648 ~ 2147483647(正负21亿),int(3) unsigned与int unsigned所储存的数值大小范围都是0 ~ 4294967295(42亿多)
并且要想让这个N有用必须开启ZEROFILL,当数据长度小于指定的N的大小时,用0从左边补全。
create table test_intN (id int(10) zerofill);
insert into test_intN values (1234);
insert into test_intN values (123456);
mysql> select id from test_intN;
+------------+
| id |
+------------+
| 0000001234 |
| 0000123456 |
+------------+
2 rows in set (0.00 sec)
tinyint(N)中的N一样。
varchar(N)、char(N)
varchar(N)中的N表示的是储存的字符串的最大长度,而不是可储存的字节大小。
varchar(3)表示可储存的字符串的最大长度是3
create table test_varcharN (name varchar(3));
insert into test_varcharN values ('123');
insert into test_varcharN values ('qwe');
insert into test_varcharN values ('我是上');
insert into test_varcharN values ('q是1');
Query OK, 1 row affected (0.01 sec)
mysql> insert into test_varcharN values ('我是韩旭');
ERROR 1406 (22001): Data too long for column 'name' at row 1
修改表中的字段属性:
alter table test_varcharN modify name varchar(10);
char(N)由于固定长度,不经常使用。
char(N)中的N代表储存需求为N的字符长度,如果长度小于N,则用空格补齐。