目录
- 1.Java中int跟integer的区别
- 2.integer在Java中有个缓存的概念,有了解吗
- 3.==跟equals的区别,equals的重写怎么进行比较
- 4.在实际开发中为什么不能用浮点类型来存金钱的数据,浮点类不精确的本质是什么
- 5.构造器能被重写吗
- 6.反射相关:class.forname和classloader的区别
- 7.jvm的内存区域相关分布,1.8以上的内存模型
- 8.内存模型中哪个数据区是内存共享的
- 9.gc时为什么需要元空间
- 10.什么情况下会从年轻代存放在老年代
- 11.用什么方法或者角度来排查频繁获取full gc
- 12.线程跟进程的区别
- 13.springboot跟tomcat是多线程还是多进程
- 14.常用的进程的通信方式有哪些?
- 15.volatile保证可见性和原子性的原理
- 16.springboot IOC和依赖注入,@bean @Autowired和@Resource注解的区别
- 17.mybatis#{}与${}的区别
- 18.AOP应用场景
- 19.innodb跟myisam
- 20.InnoDB底层实现
- 21.explain命令
- 22.redis:keys sismember命令时间复杂度
- 23.计网:tcp为什么四次挥手而不是三次 timewait在第几阶段,为什么要有2msl标志,可以修改它吗
- 25.http get跟post的区别 get请求body里能放数据吗
1.Java中int跟integer的区别
Integer是int的包装类,int则是java的一种基本数据类型
Integer的默认值是null,int的默认值是0
2.integer在Java中有个缓存的概念,有了解吗
IntegerCache.low = -128
IntegerCache.high = 127
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
3.==跟equals的区别,equals的重写怎么进行比较
equals的重写怎么进行比较
- 判断是否等于自身.
if (this == other) return true;
- 使用instanceof运算符判断 other 是否为Coder类型的对象.
if(!(other instanceof Coder))
return false;
- 比较Coder类中你自定义的数据域,name和age,一个都不能少.
Coder o = (Coder)other;
return o.name.equals(name) && o.age == age;
4.在实际开发中为什么不能用浮点类型来存金钱的数据,浮点类不精确的本质是什么
在实际开发中,不建议使用浮点类型来存储金钱的数据,因为浮点类型的计算结果可能会出现舍入误差,致计算结果不准确。这是因为浮点类型的本质是二进制浮点数,而二进制浮点数无法精确表示某些十进制小数,例如0.1这个数字在二进制浮点数中是无限循环的。
当进行浮点数计算时,由于舍入误差的存在,可能会导致计算结果与预期结果不同。这种误差会在多次计算中积,导致最终结果的误差越来越大。而在金融领域,每一分钱的计算都必须是精确的,因此不能容忍任何误差。
为了避免这种误差,应该使用Decimal类型来存储金钱的数据。Decimal类型是一种高精度的十进制浮点数类型,可以精确地表示小数点后任意位数的数字,避免了舍入误差的问题。
10进制整数转2进制是除2,10进制小数转2进制是靠乘2取1
进制转换
0.1(十进制) = 0.0001100110011001(二进制)
十进制数0.1转二进制计算过程:
0.1*2=0.2……0——整数部分为“0”。整数部分“0”清零后为“0”,用“0.2”接着计算。
0.2*2=0.4……0——整数部分为“0”。整数部分“0”清零后为“0”,用“0.4”接着计算。
0.4*2=0.8……0——整数部分为“0”。整数部分“0”清零后为“0”,用“0.8”接着计算。
0.8*2=1.6……1——整数部分为“1”。整数部分“1”清零后为“0”,用“0.6”接着计算。
0.6*2=1.2……1——整数部分为“1”。整数部分“1”清零后为“0”,用“0.2”接着计算。
0.2*2=0.4……0——整数部分为“0”。整数部分“0”清零后为“0”,用“0.4”接着计算。
0.4*2=0.8……0——整数部分为“0”。整数部分“0”清零后为“0”,用“0.8”接着计算。
0.8*2=1.6……1——整数部分为“1”。整数部分“1”清零后为“0”,用“0.6”接着计算。
0.6*2=1.2……1——整数部分为“1”。整数部分“1”清零后为“0”,用“0.2”接着计算。
0.2*2=0.4……0——整数部分为“0”。整数部分“0”清零后为“0”,用“0.4”接着计算。
0.4*2=0.8……0——整数部分为“0”。整数部分“0”清零后为“0”,用“0.2”接着计算。
0.8*2=1.6……1——整数部分为“1”。整数部分“1”清零后为“0”,用“0.2”接着计算。
……
……
所以,得到的整数依次是:“0”,“0”,“0”,“1”,“1”,“0”,“0”,“1”,“1”,“0”,“0”,“1”……。
由此,大家肯定能看出来,整数部分出现了无限循环。
5.构造器能被重写吗
构造器是不能被继承的,因为每个类的类名都不相同,而构造器名称与类名相同,所以根本谈不上继承。 又由于构造器不能继承,所以就不能被重写。
6.反射相关:class.forname和classloader的区别
(1)Class.forName除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块。
(2)而classloader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。
ClassLoader就是遵循双亲委派模型最终调用启动类加载器的类加载器,实现的功能是“通过一个类的全限定名来获取描述此类的二进制字节流”,获取到二进制流后放到JVM中。Class.forName()方法实际上也是调用的CLassLoader来实现的。
7.jvm的内存区域相关分布,1.8以上的内存模型
-
堆:用于存储对象实例,是 Java 程序中最大的一块存区域。
-
虚拟机栈:用于存储方法执行时的局部变量表、操作数栈、动态链接、返回信息等信息。
-
方法区:用于存储类的信息、常量、静态变量、即时编译器编译后的代码等数据,是线程共享的。
-
本地方法栈:与虚拟机栈类似,不过只能执行native修饰的方法。
-
程序计数器:用于记录线程执行的位置,是线程私有的。
在Java 8及以上版本中,元空间取代了方法区。元空间是一块地内存,它用于存储类的元数据信息,例如类名、访问修饰符、字段、方法、注解等信息。元空间大小可以通过-XX:MetaspaceSize和-XX:MaxMetaspaceSize参数来设置。
8.内存模型中哪个数据区是内存共享的
堆,方法区
9.gc时为什么需要元空间
在Java 8及以后的版本中,元空间(Metaspace)取代了永久代,用于存储类的元数据,如类名、方法名、字段名等。与永久代不同,元空间不再是JVM堆的一部分,而是直使用本地内存,因此不会受到JVM堆大小的限制。
GC时需要扫描元空间中的对象,因为元空间的对象也会产生垃圾,需要被回收。与永久代不同,元空间中的对象是可以被回收的,因此GC需要扫描元空间中的对象,并将不再被引用的对象进行回收。
需要注意的是,由于元空间不再是JVM堆的一部分,因此GC的实现方式也有所不同。在元空间中,GC使用的是基于标记-清除算法的垃圾回收器,不是永久代中使用的基于标记-整理算法的垃圾回收器。这是因为元空间中的对象是不连存储的,因此无法使用标记-整理算法进行回收。
10.什么情况下会从年轻代存放在老年代
- 大内存对象. 当某个对象占用的内存达到阈值时, 可能会被晋升老年代
- 年龄大的对象. 对象年龄达到阈值后(15次), 会被晋升老年代
- 动态年龄. 当某个年龄和以下年龄的对象占比超过 survivor 50%, 那么该年龄及以上的对象会晋升为老年代
- survivor 区满了, 对象会晋升为老年代. minor gc 后, eden 和 survivor 区会拷贝到另外一个 survivor 区, 此时 另外一个 survivor 区可能空间不足
11.用什么方法或者角度来排查频繁获取full gc
频繁的Full GC可能是由于多种原因引起的,以下是一些可能的排查方法或角度:
-
查看GC日志:查看GC日志可以帮助您确定Full GC发生的频率和原因。您可以使用Java虚拟机参数来启用GC日志记录,例如:-verbose:gc、-XX:+PrintGCDetails、-XX:+PrintGCDateStamps等。通过分析GC日志,您可以确定哪些对象在Full GC时被回收,以及哪些对象可能导致内存泄漏。
-
分析内存使用情况:使用Java虚拟机工具,例如jstat、jmap、jvisualvm等,可以帮助您分析内存使用情况。您可以查看堆内存使用情况、对象数量对象大小等信息,以确定哪些对象可能导致内存泄漏或者内存占用过高。
-
检查代码:检查代码可以帮您确定是否存在内存泄漏或者内存占用过高的问题。您可以使用代码审查工具,例如FindBugs、Checkstyle等,来检查代码中是否存在潜在的存泄漏或者内存占用过高的问题。
-
调整Java虚拟机参数:调整Java虚拟机参数可以帮助您优化内存使用情况。例如,您可以增加堆内存大小、调整垃圾回收算法、调整垃圾回收线程数等。但是,需要注意的是,不正确的Java虚拟机参数设置可能会导致性能下降或者内存占用过高的问题。
-
使用内存分析工具:使用内存分析工具,例如Eclipse Memory Analyzer、VisualVM等,可以帮助您分析内存使用情况,并确定哪些对象可能导致内存泄漏或者内存占用过高。这些工具可以帮助您查看对象引用关系、对象数量、对象大小等信息,以便更好地优化内存使用情况。
12.线程跟进程的区别
进程
是一个具有一定独立功能的程序关于某个数据集合的一次运行活动,是系统进行『资源分配和调度』的一个独立单位。
线程
进行运算调度的最小单位,其实是进程中的一个执行任务(控制单元),负责当前进程中程序的执行
两者之间的区别
「本质区别」:进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位
「在开销方面」:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小
「所处环境」:在操作系统中能同时运行多个进程(程序);而在同一个进程(程序)中有多个线程同时执行(通过CPU调度,在每个时间片中只有一个线程执行)
「内存分配方面」:系统在运行的时候会为每个进程分配不同的内存空间;而对线程而言,除了CPU外,系统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源
「包含关系」:没有线程的进程可以看做是单线程的,如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程
13.springboot跟tomcat是多线程还是多进程
多线程
14.常用的进程的通信方式有哪些?
如果不熟悉可以看这篇
进程的通信方式有哪些?
- 管道
- 消息队列
- 共享内存
- 信号量
- 信号
- 套接字
15.volatile保证可见性和原子性的原理
JMM(Java内存模型Java Memory Model,简称JMM)本身是一种抽象的概念并不真实存在它仅仅描述的是一组约定或规范
使用volatile修饰共享变量,就可以达到上面的效果,被volatile修改的变量有以下特点:
- 线程中读取的时候,每次读取都会去主内存中读取共享变量最新的值,然后将其复制到工作内存
- 线程中修改了工作内存中变量的副本,修改之后会立即刷新到主内存
volatile变量的读写过程
volatile变量的读写过程
read(读取)→load(加载)→use(使用)→assign(赋值)→store(存储)→write(写入)→lock(锁定)→unlock(解锁)
read: 作用于主内存,将变量的值从主内存传输到工作内存,主内存到工作内存
load: 作用于工作内存,将read从主内存传输的变量值放入工作内存变量副本中,即数据加载
use: 作用于工作内存,将工作内存变量副本的值传递给执行引擎,每当JVM遇到需要该变量的字节码指令时会执行该操作
assign: 作用于工作内存,将从执行引擎接收到的值赋值给工作内存变量,每当JVM遇到一个给变量赋值字节码指令时会执行该操作
store: 作用于工作内存,将赋值完毕的工作变量的值写回给主内存
write: 作用于主内存,将store传输过来的变量值赋值给主内存中的变量
由于上述只能保证单条指令的原子性,针对多条指令的组合性原子保证,没有大面积加锁,所以,JVM提供了另外两个原子指令:
lock: 作用于主内存,将一个变量标记为一个线程独占的状态,只是写时候加锁,就只是锁了写变量的过程。
unlock: 作用于主内存,把一个处于锁定状态的变量释放,然后才能被其他线程占用
既然一修改就是可见,为什么还不能保证原子性?
要use(使用)一个变量的时候必需load(载入),要载入的时候必需从主内存read(读取)这样就解决了读的可见性。
写操作是把assign和store做了关联(在assign(赋值)后必需store(存储))。store(存储)后write(写入)。
也就是做到了给一个变量赋值的时候一串关联指令直接把变量值写到主内存。
就这样通过用的时候直接从主内存取,在赋值到直接写回主内存做到了内存可见性。注意蓝色框框的间隙。。。。。。o(╥﹏╥)o
16.springboot IOC和依赖注入,@bean @Autowired和@Resource注解的区别
IOC
IOC—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,IOC意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。
- 谁控制谁,控制什么?
传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IOC是有专门一个容器来创建这些对象,即由IOC容器来控制对 象的创建;谁控制谁?当然是IOC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。 - 为何是反转,哪些方面反转了?
有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。
IOC的好处?
ioc的思想最核心的地方在于,资源不由使用资源者管理,而由不使用资源的第三方管理,这可以带来很多好处。第一,资源集中管理,实现资源的可配置和易管理。第二,降低了使用资源双方的依赖程度,也就是我们说的耦合度。也就是说,甲方要达成某种目的不需要直接依赖乙方,它只需要达到的目的告诉第三方机构就可以了,比如甲方需要一双袜子,而乙方它卖一双袜子,它要把袜子卖出去,并不需要自己去直接找到一个卖家来完成袜子的卖出。它也只需要找第三方,告诉别人我要卖一双袜子。这下好了,甲乙双方进行交易活动,都不需要自己直接去找卖家,相当于程序内部开放接口,卖家由第三方作为参数传入。甲乙互相不依赖,而且只有在进行交易活动的时候,甲才和乙产生联系。反之亦然。这样做什么好处么呢,甲乙可以在对方不真实存在的情况下独立存在,而且保证不交易时候无联系,想交易的时候可以很容易的产生联系。甲乙交易活动不需要双方见面,避免了双方的互不信任造成交易失败的问题。因为交易由第三方来负责联系,而且甲乙都认为第三方可靠。那么交易就能很可靠很灵活的产生和进行了。
这就是ioc的核心思想。生活中这种例子比比皆是,支付宝在整个淘宝体系里就是庞大的ioc容器,交易双方之外的第三方,提供可靠性可依赖可灵活变更交易方的资源管理中心。另外人事代理也是,雇佣机构和个人之外的第三方。
DI
依赖注入是指将依赖的对象实例交给spring帮我们注入 管理,从而释放对对象的管理权,比如可以统一替换接口的实现,更高效的开发程序
- set方法注入
- 构造方法注入
- @autowire 自动注入
如果不熟悉可以看这篇
Spring的Bean注解
用于使用Bean的注解:比如@Autowired , @Resource注解
@Autowired会先按byType去找,如果没找到,则会按照byName去找
@Resource会先按byName去找,如果没找到则会byType去找。如果设置了name属性,则只会按byName去找,找不到就报错。
17.mybatis#{}与${}的区别
- 【#】底层执行SQL语句的对象,使用PreparedStatementd,预编译SQL,防止SQL注入安全隐患,相对比较安全。
- 【 $ 】底层执行SQL语句的对象使用Statement对象,未解决SQL注入安全隐患,相对不安全。
所以 $ 和#最大的区别在于,前者是动态参数,后者是占位符, 动态参数无法防止SQL注入的问题,所以在实际应用中,应该尽可能的使用#号占位符。
另外,$符号的动态传参,可以适合应用在一些动态SQL场景中,比如动态传递表名、动态设置排序字段等。
18.AOP应用场景
记录操作日志,缓存,spring实现的事务
核心是:使用aop中的环绕通知+切点表达式(找到要记录日志的方法),通过环绕通知的参数获取请求方法的参数(类、方法、注解、请求方式等),获取到这些参数以后,保存到数据库
19.innodb跟myisam
外键 事务 锁
对比项 | MyISAM | InnoDB |
---|---|---|
外键 | 不支持 | 支持 |
事务 | 不支持 | 支持 |
行表锁 | 表锁,即使操作一条记录也会锁住整个表,不适合高并发的操作 | 行锁,操作时只锁某一行,不对其它行有影响,适合高并发的操作 |
缓存 | 只缓存索引,不缓存真实数据 | 不仅缓存索引还要缓存真实数据,对内存要求较高,而且内存大小对性能有决定性的影响 |
关注点 | 并发查询,节省资源、消耗少、简单业务 | 并发写、事务、多表关系、更大资源 |
默认安装 | Y | Y |
默认使用 | N | Y |
自带系统表使用 | Y | N |
20.InnoDB底层实现
InnoDB是MySQL的一种存储引擎,它是一个支持事务的存储引擎,具有高并发、高可靠性和高可扩展性等特点。InnoDB的底层实现主要包括以下几个方面:
-
数据存储:InnoDB将数据存储在磁盘上,并使用缓存技术提高数据访问速度。它采用了多版本并发控制(MVCC)技术,可以支持高并发的读写操作。
-
索引结构:InnoDB的索引结构是基于B+树的,它可以快速定位数据,提高查询效率。同时,InnoDB还支持自适应哈希索引,可以根据查询模式自动选择使用哈希索引或B+树索引。
-
事务管理:InnoDB支持ACID事务,可以保证数据的一致性和隔离性。它采用了多版本并发控制(MVCC)技术,可以支持高并发的读写操作。
-
锁机制:InnoDB的锁机制是基于行锁的,也就是说,它会对每一行数据进行加锁,以保证事务的隔离性和一致性。行锁是通过在索引树上加锁实现的,这样可以避免对整个表进行加锁,提高了并发性能。
-
日志系统:InnoDB的日志系统包括redo log和undo log。redo log用于恢复数据,undo log用于回滚事务。它们可以保证数据的一致性和可靠性。
21.explain命令
system>const>eq_ref>ref>range>index>ALL
system
表仅有一行记录,必须是系统表,这是const类型的特例,查询起来非常迅速。
const
表示通过索引一次就找到了,const用于primary key或者unique索引。
eq_ref
唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见主键或唯一索引扫描
ref
非唯一性索引扫描,返回匹配某个单独值的所有行. 本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而, 它可能会找到多个符合条件的行,所以他应该属于查找和扫描的混合体
range
只检索给定范围的行,使用一个索引来选择行。
Index
出现index是sql使用了索引但是没用通过索引进行过滤,一般是使用了覆盖索引或者是利用索引进行了排序分组
all
Full Table Scan,将遍历全表以找到匹配的行
22.redis:keys sismember命令时间复杂度
sismember命令:sismember命令用于判断一个元素是否存在于集合中。它的时间复杂度是O(1),也就是说,它的执行时间与集合的大小无关,可以快速地判断一个元素是否存在于集合中。
因为Redis中的集合是使用哈希表实现的,哈希表可以在常数时间内O(1)完成元素的查找操作。
23.计网:tcp为什么四次挥手而不是三次 timewait在第几阶段,为什么要有2msl标志,可以修改它吗
处于连接状态的客户端和服务端都可以发起关闭连接请求,此时需要四次挥手来进行连接关闭。
1.假设客户端主动发起连接关闭请求,他需要将服务端发起一包FIN包,表示要关闭连接,自己进入终止等待1状态,这是第一次挥手。
2.服务端收到FIN包,发送一包ACK包,表示自己进入了关闭等待状态,客户端进入终止等待2状态,这是第二次挥手。
3.服务端此时还可以发送未发送的数据,而客户端还可以接收数据,待服务端发送完数据之后,发送一包FIN包,进入最后确认状态,这是第三次挥手。
4.客户端收到之后回复ACK包,进入超时等待状态,经过超时时间后关闭连接,而服务端收到ACK包后,立即关闭连接,这是第四次挥手。
tcp为什么四次挥手而不是三次
如果是三次的话,服务器在发送消息完毕后,不能确定客户端是否收到了最后的这些消息,TCP是可靠传输,所以为了可靠一定需要一次客户端确认一次。
TIME_WAIT状态是TCP连接关闭过程中的最后一个阶段。
25.http get跟post的区别 get请求body里能放数据吗
1.参数传输方式
- get 请求把传送数据放在url中以方便记录,所以get的参数会保留在浏览器历史中
- post 请求是把传输的参数放在requrest body,post参数不会。
2.参数传输大小
- get请求大多数浏览器通常都会限制url长度在 2k,也就是 2048 字符
- post无限制
3.编码类型
- get数据类型为ASCII
- post无限制可以为二进制。
4.对于浏览器
- get可以被收藏为书签,post不行。
- get能被缓存,post不行。
5.传输数据包不同
- get 产生一个TCP数据包, 因为get 请求,浏览器会把http header 和 data一并发送出去,服务器响应200;
- post 产生两个TCP数据包,浏览器先发送 header ,服务器响应 continue 100状态码后,浏览器再发送data,服务器响应 ok 200状态码 (返回数据)。
【扩展】:为什么推荐使用 post ,好处就是:
在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,在一定程度上保证了 两次包的TCP在验证数据包的完整性上
get请求body里能放数据吗
可以放数据,但是不建议,遵循Restful规范