cyc大佬有地方写的不全,补充(JAVA基础篇)
CSDN 2024-06-30 09:05:02 阅读 92
最近看到旧电脑里存着刚毕业翻cyc的资料,一般人看完全够了,只是有些地方不太全,我做个补充
JMM的理解
JMM的核心是找到一个平衡点,在保证内存可见性的前提下,尽量的放松对编译器和处理器重排序的限制。
可能会产生缓存一致性的问题:
1.总线锁定
2.缓存一致性协议
三大特性:原子性,可见性,有序性
六.JVM的锁优化
1.自旋锁和自适应自旋锁
线程挂起和恢复都需要很大的性能开销,很多共享数据的锁定状态只持续很短的时间,为这段时间挂起恢复不值得,所以可以让现场进入一个自旋状态,但仍占用cpu的时间,默认是10次,超过10次则采用传统的方式挂起线程。自适应自旋锁会根据上次在这个锁上自旋的时间调整自选次数
2.锁消除
检测到对局部变量加锁,会自动将锁消除,如在一个方法里面有sb = s1 + s2 + s3以前会转化为StringBuffer(线程安全,有加锁)的append操作,优化之后会转换成StringBuilder的sppend操作
3.锁粗化
对一个对象反复加锁解锁,如上述的append方法,编译器会优化,只在最外层加一个锁
4.轻量级锁
对象有一个对象头,对象头分两部分区域,一部分存储子树的hashcode,GC分代年龄,锁标志位等,官方成为mark word,另一部分用于存储指向方法区对象类型的指针;
轻量级锁的执行过程是代码进入同步块的时候,如果当前对象没有被锁定(锁标志为是01)则先在线程的栈帧中建立一个锁记录空间(lock record)将对象的mark word拷贝过来,然后利用cas操作尝试将对象的markword更新为指向lock record,如果成功则当前线程拥有该对象锁,并且将锁标志位从01改为00,如果更新失败则会检查当前对象markword是否指向当前线程的栈帧,如果是则直接进入同步块执行否则说明当前的对象锁已经被其他线程占有。如果有两条以上的线程竞争这个锁,那就会膨胀成重量级锁,锁标志为变成10;
5.偏向锁
锁对象第一次被线程获取的时候,会将当前的锁标志设置为偏向锁01状态,并使用cas操作将当前线程的id记录在markword当中,如果成功那么在以后的操作当中不需进行任何操作就可以进入同步代码块,当有另外一个线程获取当前锁的时候,偏向模式就宣告结束,撤销偏向锁之后恢复到未锁定或者轻量级锁状态
String
1.string s1 = “aaa” 与 String s1 = new String(aaa);
前者会在Stringpool中创建一个对象,如果Stringpool中已经有了,则直接引用
后者会在Stringpool和堆中分别创建一个对象,如果StringPool中已经有了则只创建一个对象
2.调用s1.integer()方法,可以将字符串放入StringPool中,如果已经存在则直接返回这个对象
继承
1.几种修饰符的访问权限范围
2. == equals hashcode的关系
1.==是比较两个对象的地址是否相等
2.equals默认和 == 一样也是比较地址,也可以根据自己的需求来重写什么叫做相等
3.hashcode 是根据对象的地址来返回一串int型数字,如果对象不一样的话返回的值也不一样
3.为什么重写equals的同时也要重写hashcode
首先必须满足如果equals相同,那么hashcode也必须相同,如果hashcode不相同,那么equals必须不相同的原则,否则会导致该类无法与基于散列值的集合类(hashmap , hashset ,hashtable)结合正常运行
因为对于这些集合在进行重复性判断时,是先用hashcode判断,如果相同在用equals判断
4.hash冲突的解决方法
1.开放地址法(线性探测法,二次探测法)
2.在散列函数法
3.链地址法
5.hashtable与hashmap的区别
1.hashmap线程不安全,hashtable线程安全
2.hashmap最多允许一个键为null,而hashtable不允许
3.hashtable中默认的hash数组大小为11,增加的方式是old*2+1,而hashmap默认是16,肯定是2的指数
4.计算hash的方法不一样,hashtable是直接用hashcode,而hashmap使用了key的高16位和低16位做异或运算
Linkedhashmap继承于hashmap,可以按顺序读取,底层使用的是双向链表
TreeMap实现了sortmap接口,可以对元素进行排序,底层使用的红黑树
7.java BIO NIO AIO 序列化
字节操作:使用inputStream和outPutSream实现,字节是传输和存储的最小单位
字符操作:inputStreamReader:将字节流解码成字符流
OutPutStramWriter:将字符流编码成字节流
对象操作:序列化就是将对象转换成字节序列,方便存储和运输,两个用途:
1.把对象的字节序列永久地保存到硬盘中,通常存放在一个文件里
2.在网络上传送对象的字节序列
在很多应用中,需要对某些对象进行序列化,让他们离开内存空间,入住到物理磁盘方便长期保存,比如session对象,当有大量用户并发访问的时候可能会出现10万个session对象,内存吃不消,此时就需要将这些session先序列化到硬盘中,等要用的时候在把对象还原到内存中。
当两个进程在进行远程通信的时候,彼此可以发送各种类型的数据,无论是何种类型的数据,都会以二进制的序列在网络上传送。发送方需要把这个java对象转换成字节序列,才能在网络上传送;接收方则需要把字节序列恢复成java对象。
Io与NIO的区别:
1.NIO是非阻塞的
2.NIO是面向缓冲区的,IO是面向流的
AIO:异步非阻塞
8.static关键字的作用
一.修饰变量:
1.静态变量在类加载的时候被创建并初始化,只被创建一次(类加载只进行一次),可以修改
2.静态变量属于整个类而不属于某个对象。
二.修饰方法
1.可以通过类名来访问,不需要创建对象来访问,可以用来实现单例模式
2.静态方法只能调用静态方法和静态变量
三.修饰静态代码块
在类加载的时候执行,且只执行一次
9.单例模式
实现:
1.为什么要用判断双重:
因为可能有两个线程都执行完了第一个if语句,如果没有第二重判断,那么当其中有个线程执行完synchronized里面的语句之后,另外一个线程跟着也会执行,这样就达不到单例模式的效果
2.第一重判断去掉也可以实现,为什么不去掉
这个设计性能问题,因为
参考:单例模式双重检查锁机制 | 秦江波的个人博客
10.this与super关键字
9.java中的多态
分为两种:
1.编译时多态:体现在重载(方法名相同而参数不同),在编译时期就根据传入的参数确定好调用哪个方法;
2.运行时多态:体现在方法的重写。在运行时期判断引用类型的实际类型根据实际的类型调用其相应的方法;
当父类对象引用变量引用子类对象时,被引用对象的类型决定了调用谁的成员方法,引用变量类型决定可调用的方法。如果子类中没有覆盖该方法,那么会去父类中寻找。
参考链接:Java 中的多态 | 菜鸟教程
10.接口类和抽象类的异同
区别:
1.抽象类可以有非抽象方法,但接口只能有抽象方法
2.接口中的成员变量默认修饰为public static final(高度抽象的模版,所以这些都是提取出来的不变特征),方法默认修饰为public abstract。抽象类中的成员变量可以被不同的修饰符来修饰
3.类可以实现多个接口但只能继承一个抽象类
相同:
1.不能被实例化
2.派生类都必须实现未实现的方法
11.instanceof
用来判断一个对象是否是一个类的实例
12.各种排序算法复杂度及稳定性
1.java底层如何实现排序的
Java中Arrays.sort使用了两种排序方法,快速排序和优化归并排序。
快速排序主要针对基本的数据类型(int short long)排序,而归并排序用于对象类型的排序
如果数据量小于60会使用插入排序,插入排序是稳定的
13.java中的堆和栈
栈:主要用于存储局部变量和对象的引用变量,每个线程都有一个独立的栈空间,所以线程之间是不共享数据的;
栈的特点是存取速度快,但所存数据的大小和生存期必须是确定的(编译后就已经确定大小)
堆:堆中主要存储实例化的对象和数组。线程共享。堆的优势是可以动态的分配内存大小,生存期也不必事先告诉编译器,但缺点是存取速度较慢
Linux基本面试题:
Ls:用于显示指定工作目录下的类容,不会列出详细信息
Ll:会列出当前文件目录的详细信息,含有时间,权限,大小等
Cd:用于切换到目标目录
Mkdir:用于简历当前目录的子目录
rm-r 删除的文件或者目录,需确认
rm –rf同上但无需确认
cp:复制文件 若复制目录则必须加上-r
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。