abstract class在Java语言中表示的是一种继承关系,一个类只能使用一次继承关系。但是,一个类却可以实现多个interface。
接口和抽象类的概念不一样。接口是对动作的抽象,抽象类是对根源的抽象。从设计理念上,接口反映的是 “like-a” 关系,抽象类反映的是 “is-a” 关系。 抽象类表示的是,这个对象是什么。接口表示的是,这个对象能做什么
即:
abstract class (名词),被继承
interface(动词),被实现
————————————深拷贝和浅拷贝———————————–
Java中有三种类型的对象拷贝:
浅拷贝(Shallow Copy)、深拷贝(Deep Copy)、延迟拷贝(Lazy Copy)。
什么是浅拷贝
浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。
如果属性是基本类型,拷贝的就是基本类型的值;
如果属性是内存地址(引用类型),拷贝的就是内存地址 ,
因此如果其中一个对象改变了这个地址,就会影响到另一个对象。
什么是深拷贝
深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。
当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。
简单理解一:
在有指针的情况下,浅拷贝只是增加了一个指针指向已经存在的内存,而深拷贝就是增加一个指针并且申请一个新的内存,使这个增加的指针指向这个新的内存,采用深拷贝的情况下,释放内存的时候就不会出现在浅拷贝时重复释放同一内存的错误!
简单理解二 :
浅拷贝 只拷贝指针,深拷贝就是拷贝他的值,重新生成的对像。就像是浅拷贝就是你的影子,深拷贝是你的克隆人,你没了影子也就没了,但是克隆人还活着。
————————————— transient———————————
对象序列号需要实现Serilizable接口
将不需要序列化的属性前添加关键字transient,该属性就不会序列化
拓展:
我们知道在Java中,对象的序列化可以通过实现两种接口来实现,若实现的是Serializable接口,则所有的序列化将会自动进行。
若实现的是Externalizable接口,则没有任何东西可以自动序列化,需要在writeExternal方法中进行手工指定所要序列化的变量,这与是否被transient修饰无关。
因此transient只适用于Serializable接口序列化方式
—————————–Java finally与return执行顺序———————–
总结:
1、finally语句在return语句执行之后return返回之前执行的。
2、finally块中的return语句会覆盖try块中的return返回。
3、 如果finally语句中没有return语句覆盖返回值,那么原来的返回值可能因为finally里的修改而改变也可能不变。
最后总结:
finally块的语句在try或catch中的return语句执行之后返回之前执行且finally里的修改语句可能影响也可能不影响try或catch中 return已经确定的返回值,若finally里也有return语句则覆盖try或catch中的return语句直接返回。
————————————Java8新特性———————————–
Lambda表达式和函数式接口
接口的默认方法和静态方法
方法引用
重复注解
更好的类型推断
拓宽注解的应用场景
二、JAVA并发
——————————-Java创建线程的三种方式—————————-
1、继承Thread类
2、实现Runnable接口
3、通过Callable和Future创建线程
创建线程的三种方式的对比
采用实现Runnable、Callable接口的方式创见多线程时,
优势是:
线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。
在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
劣势是:
编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法。
使用继承Thread类的方式创建多线程时
优势是:
编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。
劣势是:
线程类已经继承了Thread类,所以不能再继承其他父类。
——————————————Java线程池———————————
定义:
在Android中,由于主线程的诸多限制,像网络请求等一些耗时的操作我们必须在子线程中运行。
我们往往会通过new Thread来开启一个子线程,待子线程操作完成以后通过Handler切换到主线程中运行。
这么以来我们无法管理我们所创建的子线程,并且无限制的创建子线程,它们相互之间竞争,很有可能由于占用过多资源而导致死机或者OOM。
所以在Java中为我们提供了线程池来管理我们所创建的线程。
线程池的优势
①降低系统资源消耗,通过重用已存在的线程,降低线程创建和销毁造成的消耗;
②提高系统响应速度,当有任务到达时,无需等待新线程的创建便能立即执行;
③方便线程并发数的管控,线程若是无限制的创建,不仅会额外消耗大量系统资源,更是占用过多资源而阻塞系统或oom等状况,从而降低系统的稳定性。线程池能有效管控线程,统一分配、调优,提供资源使用率;
④更强大的功能,线程池提供了定时、定期以及可控线程数等功能的线程池,使用方便简单。
关键字:ThreadPoolExecutor
四种线程池类
1. newFixedThreadPool(数量固定,都是核心线程)
2.newCachedThreadPool(缓存,核心线程数为0,都是非核心线程)
3. newScheduledThreadPool(固定核心线程,非核心线程无限制)
4. newSingleThreadExecutor(单核心线程,一个一个任务排队等待)
—————————————死锁———————————————
在JAVA编程中,有3种典型的死锁类型:
静态的锁顺序死锁(两个线程,A方法等B方法,B方法等A方法)
动态的锁顺序死锁(两个线程调用同一个方法时,入参颠倒造成的死锁)
协作对象之间发生的死锁。(线程1持有A对象锁并等待B对象锁,线程2持有B对象锁并等待A对象锁。)
避免死锁:
在写代码时,要确保线程在获取多个锁时采用一致的顺序。同时,要避免在持有锁的情况下调用外部方法。
—————————————线程同步—————————————-
Java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时
(如数据的增删改查),将会导致数据不准确,相互之间产生冲突。
加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,从而保证了该变量的唯一性和准确性。
一共有两种锁,来实现线程同步问题,分别是:synchronized和ReentrantLock
synchronized实现同步的基础:
Java中每个对象都可以作为锁。当线程试图访问同步代码时,必须先获得对象锁,退出或抛出异常时必须释放锁。
Synchronzied实现同步的表现形式分为:代码块同步 和 方法同步。
ReentrantLock,一个可重入的互斥锁,它具有与使用synchronized方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。
————————————–线程间通信的两种方式———————————–
Object类中相关的方法有两个:
notify方法和三个wait方法
拓展:
http://blog.csdn.net/yang_teng_/article/details/53325280
————————————–volatile关键字———————————-
当一个变量定义为 volatile 之后,将具备两种特性:
1.保证此变量对所有的线程的可见性
当一个线程修改了这个变量的值,volatile 保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。
但普通变量做不到这点,普通变量的值在线程间传递均需要通过主内存
2.禁止指令重排序优化。
有volatile修饰的变量,赋值后多执行了一个“load addl $0x0, (%esp)”操作,这个操作相当于一个内存屏障(指令重排序时不能把后面的指令重排序到内存屏障之前的位置)
只有一个CPU访问内存时,并不需要内存屏障;(什么是指令重排序:是指CPU采用了允许将多条指令不按程序规定的顺序分开发送给各相应电路单元处理)。
更多:
Java语言提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新操作通知到其他线程。
当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。
volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。
在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此volatile变量是一种比sychronized关键字更轻量级的同步机制。
详见:
https://www.cnblogs.com/zhengbin/p/5654805.html
——————————–乐观锁与悲观锁——————————-
悲观锁,正如其名,它指的是对数据被外界修改持保守态度(悲观),因此,在整个数据处理过程中,将数据处于锁定状态。
悲观锁的实现,往往依靠数据库提供的锁机制 (也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)
优点与不足
悲观并发控制实际上是“先取锁再访问”的保守策略,为数据处理的安全提供了保证。
但是在效率方面,处理加锁的机制会让数据库产生额外的开销,还有增加产生死锁的机会;
另外,在只读型事务处理中由于不会产生冲突,也没必要使用锁,这样做只能增加系统负载;还有会降低了并行性,一个事务如果锁定了某行数据,其他事务就必须等待该事务处理完才可以处理那行数
乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信息,让用户决定如何去做。
相对于悲观锁,在对数据库进行处理的时候,乐观锁并不会使用数据库提供的锁机制。一般的实现乐观锁的方式就是记录数据版本。
优点与不足
乐观并发控制相信事务之间的数据竞争(data race)的概率是比较小的,因此尽可能直接做下去,直到提交的时候才去锁定,所以不会产生任何锁和死锁。但如果直接简单这么做,还是有可能会遇到不可预期的结果,例如两个事务都读取了数据库的某一行,经过修改以后写回数据库,这时就遇到了问题。
详见:
http://www.hollischuang.com/archives/934
——————————-AbstractQueuedSynchronizer—————————-
AQS用来实现锁或其他同步组件的基础框架(注意区别synchronized是在字节码上加指令方式,通过底层机器语言保证同步)。
AQS使用int类型的volatile变量维护同步状态(state),使用Node实现FIFO队列来完成线程的排队执行。在锁的实现中通过组合AQS对象的方式使用,利用AQS实现锁的语义。
AQS与锁(如Lock)的对比:
锁是面向使用者的,锁定义了用户调用的接口,隐藏了实现细节;
AQS是锁的实现者,通过用AQS简化了锁的实现屏蔽了同步状态管理,线程的排队,等待唤醒的底层操作。
简而言之,锁是面向使用者,AQS是锁的具体实现者。
——————————-深入理解ReentrantLock—————————-
重入锁(ReentrantLock)是一种递归无阻塞的同步机制
http://blog.csdn.net/yanyan19880509/article/details/52345422
———————————Java并发集合——————————–
Java并发集合——ArrayBlockingQueue ,LinkedBlockingQueue,ConcurrentHashMap
http://blog.csdn.net/u013277740/article/details/79367237
三、JAVA虚拟机
本部分内容是关于Java虚拟机的一些面试高频知识点的总结。说到对Java虚拟机的学习,就不得不提下这本书《深入理解Java虚拟机》。
本部分的内容也是基于这本书进行整理的,这本书基本是面试必备。+
关于Java虚拟机,重点考察以下三个方面的内容:
内存区域/内存模型
类加载机制
垃圾收集算法/收集器
目录
–
-
对象的创建、内存布局和访问定位
-
Java内存区域与内存模型
-
Java类加载机制及类加载器详解
-
JVM中垃圾收集算法及垃圾收集器详解
-
JVM怎么判断对象是否已死?
———————对象的创建、内存布局和访问定位——————-
一、对象的创建
,虚拟机遇到一个new指令时,
1、检查指令在常量池中代表的类是否已经被加载,解析和初始化过,如果没有,那必须先执行响应的类加载过程;
2.在类加载检查功通过后,为新生对象分配内存。对象所需的内存大小在类加载完成后便可完全确定。
二、对象的内存布局
分为3个区域:
对象头
实例数据(存储的有效信息)
对齐填充
三、对象的访问定位
Java程序需要通过栈上了reference数据来操作堆上的具体对象。
目前主流的访问方式有两种
句柄(句柄池)
直接指针(对象地址)
——————————-Java内存区域与内存模型 —————————
Java内存区域
方法区(公有):被虚拟机加载的类信息,常量,静态常量,即编译后的代码等数据。
堆(公有): 存放实例对象,Java堆是垃圾收集器管理的主要区域,因此很多时候也被称为“GC堆”。
虚拟机栈(线程私有): java方法执行时创建的一个栈帧,每一个方法从调用直至完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
本地方法栈(线程私有): 与虚拟机栈所发挥的作用相似,
虚拟机栈为虚拟机执行java方法,而本地方法栈为虚拟机使用到的Native方法服务。
程序计数器(线程私有): 一块较小的内存,当前线程所执行的字节码的行号指示器。
字节码解释器工作时,就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。
Java内存模型
Java内存模型的目的: 让java程序在各种平台下都能达到一致的内存访问效果。
主要目标: 定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节。
Java内存模型规定了所有的变量都存储在主内存中。
每条线程中还有自己的工作内存,线程的工作内存中保存了被该线程所使用到的变量(这些变量是从主内存中拷贝而来)。
线程对变量的所有操作(读取,赋值)都必须在工作内存中进行。不同线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成。
—————————Java类加载机制及类加载器 ————————-
一、类加载机制
把类从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型。
在Java语言里,类型的加载、连接和初始化过程都是在程序运行期间完成的
二、类加载器
类加载机制中加载部分的功能是将类的class文件读入内存,并为之创建一个java.lang.Class对象。
这部分功能就是由类加载器来实现的。
# 总结
我个人认为,如果你想靠着背面试题来获得心仪的offer,用癞蛤蟆想吃天鹅肉形容完全不过分。想必大家能感受到面试越来越难,想找到心仪的工作也是越来越难,高薪工作羡慕不来,却又对自己目前的薪资不太满意,工作几年甚至连一个应届生的薪资都比不上,终究是错付了,错付了自己没有去提升技术。
这些面试题分享给大家的目的,其实是希望大家通过大厂面试题分析自己的技术栈,给自己梳理一个更加明确的学习方向,当你准备好去面试大厂,你心里有底,大概知道面试官会问多广,多深,避免面试的时候一问三不知。
大家可以把Java基础,JVM,并发编程,MySQL,Redis,Spring,Spring cloud等等做一个知识总结以及延伸,再去进行操作,不然光记是学不会的,这里我也提供一些脑图分享给大家:
![](https://img-blog.csdnimg.cn/img_convert/73d2eddcd32816fd08f65ba196c01a4a.webp?x-oss-process=image/format,png)
![](https://img-blog.csdnimg.cn/img_convert/47e3333fe73909a7b1298943e60a0175.webp?x-oss-process=image/format,png)
![](https://img-blog.csdnimg.cn/img_convert/ad8f7cf0339de4aca05412ecbab7b3e2.webp?x-oss-process=image/format,png)
希望你看完这篇文章后,不要犹豫,抓紧学习,复习知识,准备在明年的金三银四拿到心仪的offer,加油,打工人!
以被虚拟机直接使用的Java类型。
在Java语言里,类型的加载、连接和初始化过程都是在程序运行期间完成的
二、类加载器
类加载机制中加载部分的功能是将类的class文件读入内存,并为之创建一个java.lang.Class对象。
这部分功能就是由类加载器来实现的。
# 总结
我个人认为,如果你想靠着背面试题来获得心仪的offer,用癞蛤蟆想吃天鹅肉形容完全不过分。想必大家能感受到面试越来越难,想找到心仪的工作也是越来越难,高薪工作羡慕不来,却又对自己目前的薪资不太满意,工作几年甚至连一个应届生的薪资都比不上,终究是错付了,错付了自己没有去提升技术。
这些面试题分享给大家的目的,其实是希望大家通过大厂面试题分析自己的技术栈,给自己梳理一个更加明确的学习方向,当你准备好去面试大厂,你心里有底,大概知道面试官会问多广,多深,避免面试的时候一问三不知。
大家可以把Java基础,JVM,并发编程,MySQL,Redis,Spring,Spring cloud等等做一个知识总结以及延伸,再去进行操作,不然光记是学不会的,这里我也提供一些脑图分享给大家:
[外链图片转存中...(img-Hf8l0c5D-1714135680692)]
[外链图片转存中...(img-qsdpLVs5-1714135680692)]
[外链图片转存中...(img-CmkEo1V1-1714135680693)]
希望你看完这篇文章后,不要犹豫,抓紧学习,复习知识,准备在明年的金三银四拿到心仪的offer,加油,打工人!
> **本文已被[CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】](https://bbs.csdn.net/topics/618154847)收录**