面试题总结1 ----java基础阶段

基础

重要

JDk和JRE的区别
JDK : JDK是整个java的核心, 包括了java运行环境JRE, 一堆java工具和java基础的类库. 通过JDK开发人员将源码文件编译成字节文件 源码java 字节class
JRE: JRE是java运行环境, 不含开发环境,即便没有编译器和调试器, 将class文件加载到内存准备运行
讲一讲Java内存的堆(heap)、栈(stack)和方法区(method)
栈:调用方法将在栈中开辟内存,称为入栈(压栈)。
栈内存存放基本类型值和引用数据类型的地址。
栈内存中的数据,没有默认初始化值,需要手动设置。
方法调用完成,栈内存立即释放,称为出栈(弹栈)
堆:用于存放使用new创建的对象或数组。
所有的对象都有内存地址值。
数据都有默认初始化值。
堆内存中的对象不再被指向时,JVM启动垃圾回收机制,自动清除。
方法区:与Java堆一样,是各个线程共享的内存区域。
存储已被Java虚拟机加载的类信息、常量、静态变量、以及编译器编译后的代码等。

GC是什么? 为什么要有GC?
GC是垃圾收集的意思。
是指JVM用于释放那些不再使用的对象所占用的内存。垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存。Java有了GC,就不需要程序员去人工释放内存空间。

说说&和&&的区别
& 位运算,求2个二进制数的与。也可以进行逻辑运算,表达式左边为false,表达式右边继续执行。
&& 逻辑运算符,表达式左边为false,整个表达式结果为false,因此表达式右边不执行。此现象称为逻辑短路。

super与this的区别
1.this表示对象本身,用来调用当前类里的属性、方法,也可以调用从父类继承而来的未被重写、非私有的方法;
super表示当前对象类的父类对象部分,用来调用父类里的属性、方法;
2.this和super用来调用构造器方法
不同点:super()从子类中调用父类的构造方法,this()在同一类内调用其它构造方法。
相同点:super()和this()都必须在构造函数的第一行进行调用,否则就是错误的

"=="和equals方法究竟有什么区别?
比较的是值
== 如果比较的是基本数据类型,比较的则是变量值
== 如果比较的为引用数据类型,比较的则是地址值
equals比较的是引用数据类型
如果没有重写hashCode和equals方法,比较的是地址值。因为Object的equals方法中使用是

 如果重写hashCode和equals方法,则比较的重写后的规则。
 例如:两个String字符串比较时:比较的是内容。因为String底层重写了equals方法进行内容的比较。

static和final关键字
static关键字为静态的
1.用来修饰成员变量,静态变量所有对象共享;
2.用来修饰成员方法,可通过“类名.方法名”的方式调用,常用于工具类;
3.静态代码块,在类被加载的时候就会被执行的代码块;
final关键字
1.用来修饰数据,包括成员变量和局部变量,该变量只能被赋值一次且它的值无法被改变。对于成员变量来讲,必须在声明时或者构造方法中对它赋值;
2.修饰方法,表示该方法无法被重写;
3.修饰类,表示该类无法被继承。

List, Set, Map的区别
1、List中的元素,有序、可重复、可为空;
2、Set中的元素,无序、不重复、只有一个空元素;
3、Map中的元素,无序、键不重,值可重、可一个空键、多个空

ArrayList和LinkedList有什么区别?
1, ArrayList的底层是动态数组;LinkedList的底层是双向链表
2, ArrayList默认初始大小为10,默认扩容大小为1.5倍;LinkedList将元素添加到链表的末尾,无须扩容
3, ArrayList查询快, 增删慢, LinkedList增删除快, 查询慢

如何对list集合中的数据进行去重
1.借助set集合
2.利用List的contains方法循环遍历

数组和链表分别适用于什么场景,为什么
数组:查询多,增删少; 数组的特性
链表:查询少,增删多; 链表的特性

HashMap的底层结构
HashMap底层数据结构为数组+链表;在JDK1.8中当链表的长度超过8时,链表会转换为红黑树;

HashMap怎么将数据存在有限的数组中
(HashMap如何存储数据?)

1、如果数组还没有初始化(数组长度是0),则先初始化,默认16
2、通过hash方法计算key的hash值,hash值对数组长度进行取余操作,进而计算得到应该放置到数组的位置
3、如果该位置为空,则直接放置此处
4、如果该位置不为空,而且元素是红黑树,则插入到其中
5、如果是链表,则遍历链表,如果找到相等的元素则替换,否则插入到链表尾部
6、如果链表的长度大于或等于8,则将链表转成红黑树

HashMap在JDK1.8与JDK1.7中有什么不同
1、1.8中引入了红黑树,而1.7中没有
2、1.8中元素是插在链表的尾部,而1.7中新元素是插在链表的头部
3、扩容的时候,1.8中不会出现死循环,而1.7中容易出现死循环

HashMap和HashTable的区别
1)HashMap是非线程安全的,HashTable是线程安全的,内部的方法基本都经过synchronized修饰。
(2)因为同步、哈希性能等原因,HashMap的性能优于HashTable
(3) HashMap允许有null值的存在,在HashTable不允许有null值
(4)HashMap默认初始化数组的大小为16,HashTable为11。前者扩容时乘2,使用位运算取得哈希,效率高于取模。而后者为乘2加1,都是素数和奇数,这样取模哈希结果更均匀。

TCP和UDP的区别
1、TCP(面向连接如打电话要先拨号建立连接),建立TCP连接需经过三次握手,释放TCP连接需经过四次挥手;UDP是无连接的,即发送数据之前不需要建立连接
2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
Tcp通过校验和,重传控制,序号标识,滑动窗口、确认应答实现可靠传输。如丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。
3、UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。
4.每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5、TCP对系统资源要求较多,UDP对系统资源要求较少。

非重要

在java中,对象什么时候可以被垃圾回收?
一般情况下java中对象可被回收的前提是:该对象不再被引用。然后垃圾回收器在回收的时候便会把这个对象清理掉
垃圾回收机制的算法:(扩展)
1 引用计数算法:
给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器都为0的对象就是不再被使用的,垃圾收集器将回收该对象使用的内存。
2 根搜索算法:
通过一系列的名为“GC Root”的对象作为起点,从这些节点向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Root没有任何引用链相连时,则该对象不可达,该对象是不可使用的,垃圾收集器将回收其所占的内存。

java中会存在内存泄漏吗,请简单描述。
会存在,
内存泄漏是指程序分配的内存未释放或无法释放的现象。
java中内存泄露的发生场景:全局的集合变量、不正确的单例模式的使用

Java中内存泄漏产生的原因可能有哪些?
递归的时候会引起栈内存溢出

1、静态集合类引起内存泄漏;
2、当集合里面的对象属性被修改后,再调用remove()方法时不起作用;
3、监听器
4、各种连接
5、内部类和外部模块的引用
6、单例模式
具体说明请自行查看原文,链接:https://blog.csdn.net/c_royi/article/details/79527518

*面向对象的基本特征是什么/什么是面向对象/你是如何理解面向对象的
封装 有选择的隐藏和暴露一些数据和方法
继承 子类可以直接实现父类中的方法,有选择的扩展
多态 调用同一个方法展示出来不同的方式。
抽象 把共同的特性抽取到一个类中
讲一下你对OOP的理解
OOP(object oriented programming),即面向对象编程
面向对象具有四大特性,分别是
1.抽象
将一些事物的共性抽离出来归为一个类。
如对于动物,具有生命体征、活动能力等区别于其它事物的共同特征
2.封装
有选择地隐藏和暴露数据和方法
比如有U盘这个类,我希望隐藏内部组成和实现,只暴露USB接口以供使用
3.继承
子类可以直接使用父类的部分数据和方法,可以有选择的扩展
比如鸟是动物,但鸟扩展了飞行的能力。
4.多态
同一类的对象调用相同方法可以表现出不同的行为
比如动物实现了say()方法,猴子、马等动物重写了say()方法来表现不同的交流语言。
Java中实现多态的机制是什么?
Java中,靠的是父类或接口定义的引用变量可以指向子类或具体实现类的实例对象,而程序调用的方法在运行期才动态绑定,就是引用变量所指向的具体实例对象的方法,也就是内存里正在运行的那个对象的方法,而不是引用变量的类型中定义的方法。
Overload和Override的区别?
方法重写(overriding):
  1、也叫子类的方法覆盖父类的方法,要求返回值、方法名和参数都相同。
  2、子类抛出的异常不能超过父类相应方法抛出的异常。(子类异常不能超出父类异常)
  3、子类方法的的访问级别不能低于父类相应方法的访问级别(子类访问级别不能低于父类访问级别)
方法重载(overloading):
重载是在同一个类中的两个或两个以上的方法,拥有相同的方法名,但是参数却不相同,方法体也不相同,最常见的重载的例子就是类的构造函数
构造器是否可被override
Constructor(构造器)不能被继承,所以不能被override(重写),但是可以被overloading(重载)。
抽象类和接口的区别
抽象类是什么
含有abstract修饰符的class即为抽象类。
特点:

  1. 不能创建的实例对象
  2. 可以有抽象方法
  3. 可以有构造方法,但构造方法不能是抽象的
  4. 抽象方法必须子类进行实现,如果子类只实现部分方法,该子类必须是抽象的。
    接口是什么
    含有interface修饰符的就是 。
    特点:
  5. 接口中的所有方法都必须是抽象的
  6. 接口中的方法定义默认为public abstract类型
  7. 接口中的成员变量类型默认为public static final
    区别是什么
  8. 单继承多实现:一个类可以实现多个接口,但只能继承一个抽象类。
  9. 构造方法:抽象类可以有构造方法,接口中不能有构造方法。(JDK7之前)
  10. 变量:抽象类中可以有普通成员变量,接口中只有常量。
  11. 方法:抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。
  12. 修饰符:抽象类中的抽象方法可以使用public和protected进行修饰,但接口中的抽象方法只能使用public abstract修饰。
  13. 静态方法:抽象类中可以包含静态方法,接口中不能包含静态方法
  14. 抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。
    (注:JDK1.8新特性,接口可以有默认方法和静态方法)
    抽象类不能被实例化那他的构造器是用来干什么的
    子类继承抽象类时,构造函数不会被覆盖。
    而且,在实例化子类对象时首先调用的是抽象类中的构造函数再调用子类中的。
    在这个阶段初始化抽象类字段或执行其它与子类相关的代码
    Java中有没有多继承
    Java的类是单继承的,即每个类只能继承一个类。
    但是,Java中接口支持多继承,即一个接口可以继承多个接口
    四类八种?及其取值范围
    1.整型:byte(1个字节)、short(2个字节)、int(4个字节) 、long(8个字节)
    2.浮点型:float(4个字节)、double(8个字节)
    3.字符型:char(2个字节)
    4.布尔型:boolean(1个字节)
    byte的取值范围为-128~127(-2的7次方到2的7次方-1)
    short的取值范围为-32768~32767(-2的15次方到2的15次方-1)
    int的取值范围为(-2147483648~2147483647)(-2的31次方到2的31次方-1)
    long的取值范围为(-9223372036854774808~9223372036854774807)(-2的63次方到2的63次方-1)
    单精度:float 3.402823e+38 ~ 1.401298e-45
    双精度:double 1.797693e+308~ 4.9000000e-324
    char:0-65535
    Integer与int的区别
    1.int是Java的数据类型之一,Integer是Java为int提供的封装类;
    2.int的初始化值为0,而Integer的初始化值为null。
    数值提升是什么?
    数值提升是指数据从一个较小的数据类型转换成为一个更大的数据类型。
    byte,char,short值会被转化成int类型。需要的时候int类型也可能被提升成long。long和float则有可能会被转换成double类型。
    什么是隐式转换,什么是显式转换
    子类继承抽象类时,构造函数不会被覆盖。
    而且,在实例化子类对象时首先调用的是抽象类中的构造函数再调用子类中的。
    在这个阶段初始化抽象类字段或执行其它与子类相关的代码
    如何将一个字符串变成int?
    Integer.parseInt()
    讲一讲运算符:++i,i++,+=
    “++i”:先自增,后赋值
    “i++”:先赋值,后自增
    “+=”:a+=b -> a = a+b +=还带有自动类型转换
    字符串String s =“sfds55d676455dfg32434eertre”; 怎么样把非数字全部去除,只保留数字?
    正则(扩展 如:s.replaceAll(“[^0-9]“,”"),这样非数字就全部去除了,只保留数字了
    String、StringBuilder、StringBuffer的区别?
    String:String类被final修饰不能被继承,String内部char[]被final修饰,字符串内容无法被修改
    StringBuffer:可变字符串、效率低、线程安全;
    StringBuilder:可变字符序列、效率高、线程不安全;
    说一说你对权限修饰符的了解
    java中4中修饰符分别为public、protect、default、private
    访问权限 类 包 子类 其他包
    public ∨ ∨ ∨ ∨
    protect ∨ ∨ ∨ ×
    default ∨ ∨ × ×
    private ∨ × × ×
    静态变量和成员变量的初始化时机
    静态初始化只在Class对象首次被加载的时候进行一次。
    成员变量初始化,在new对象的时候被初始化,在构造函数的隐式三步其中一步进行初始化,初始化完成之后才会执行构造器中的代码
    获取一个类的类对象有哪几种方式
    1.通过全路径类名的字符串获取对象,Class clazz = Class.forName(“com.czxy.pojo.User”)
    2.通过类名加.class获取对象,Class clazz = User.class
    3.通过对象来获得类对象,Class clazz = new User() .getClass();
    4.通过子类的实例获取父类的类对象,
    User cn = new User();
    Class userClass = cn.getClass();
    Class subUserClass = userClass.getSuperclass();
    创建对象的有哪几种方式
    1.使用new关键字
    2.使用反射创建对象
    3.使用Clone的方式创建:用clone方法创建对象并不会调用任何构造函数。
    4.使用反序列化:反序列化时,JVM创建对象并不会调用任何构造函数。
    克隆clone和new对象的区别
    1, clone()不会调用构造方法;new会调用构造方法。
    2, new对象时根据类型确定分配内存空间的大小, clone是根据原对象分配内存
    克隆clone和new对象的区别
    1, clone()不会调用构造方法;new会调用构造方法。
    2, new对象时根据类型确定分配内存空间的大小, clone是根据原对象分配内存
    为什么要克隆?如何实现clone
    1.为什么要克隆?
    ①方便,克隆的对象可能包含一些已经修改过的属性,而new出来的对象的属性都还是初始化时候的值,所以当需要一个新的对象来保存当前对象的“状态”使用clone方式很方便;
    ②速度快,clone方法最终将调用JVM中的原生方法完成复制也就是调用底层的c++代码,所以一般使用clone方法复制对象要比新建一个对象然后逐一进行元素复制效率要高。
    2.实现clone
    ① 实现Cloneable接口并重写Object类中的clone()方法;
    ② 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。对象序列化后写入流中,再从流中读取,生成新的对象,新对象和原对象之间也是完全互不影响的。
    深克隆和浅克隆的区别?
    浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
    深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址
    当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?
    是值传递, Java中只有值传递,没有引用传递概念(引用数据类型传递的是地址值,基本数据类型直接传递值)
    列举5种常见的运行时异常
    (1)java.lang.NullPointerException 【空指针异常】
    (2)java.lang.ClassNotFoundException 【类找不到异常】
    (3)java.lang.NumberFormatException 【数字格式化异常】
    (4)java.lang.IndexOutOfBoundsException 【数组角标越界异常】或 【数组索引越界异常】
    (5)java.lang.IllegalArgumentException 【非法参数异常】
    (6)java.lang.ClassCastException 【类型转换异常】
    (7)java.lang.NoClassDefFoundException 【类未定义异常】
    (8)SQLException 操作数据库异常 【SQL异常】
    (9)java.io.IOException 【IO异常】
    (10)java.lang.NoSuchMethodException 【没有匹配方法异常】
    error和exception有什么区别?
    1, exception可以通过try…catch捕获, 进行相应的处理, 或者抛出由调用者处理
    2, error 不可控制, 无法处理
    运行时异常与checked检查型异常有何异同?
    checked检查型异常,又称为编译时异常,必须对异常进行处理(try-catch 或 throws)
    运行时异常,不被检查型的异常,对异常可以处理,也可以不处理。
    如何自定义异常
    自定义检查性异常类,继承 Exception 类。
    自定义运行时异常类,继承 RuntimeException 类。
    final, finally, finalize的区别
    (1)final:修饰符。
    A).如果一个类被声明为final,就意味着它不能再派生出新的子类,不能作为父类被继承。
    B).如果将变量或者方法声明为final,可以保证它们在使用中不被改变。
    C).被声明final的方法只能使用,不能重写。
    (2)finally:异常处理机制的一部分
    A).finally结构使代码总会执行,而不管是否有异常。
    B).使用finally常用于释放资源。
    (3)finalize:Java中垃圾回收器执行的方法。
    A).它是一个方法,属于java.lang.Object类。
    B).在垃圾收集器执行的时候会调用的此方法,进行对象的回收,但在调用垃圾回收gc()后,并不能立即执行回收,JVM根据算法定时执行。
    实例化数组后,能不能改变数组长度
    不能,数组一旦实例化,它的长度就是固定的
    while循环和do循环有什么不同?
    while结构在循环的开始判断下一个迭代是否应该继续。
    do/while结构在循环的结尾来判断是否将继续下一轮迭代。do结构至少会执行一次循环体。
    break和continue
    (1)break:
    常用于循环中,含义:结束循环,跳出循环体
    其他应用场景:switch语句中,结束语句。
    (2)continue:
    结束本次循环,进行下一次循环;(注意:如果,循环进行到最后一次,continue执行,结束本次循环, 继续进行循环逻辑判断结束循环。循环结束与continue无关)
    for循环和增强for哪一个遍历list集合更快,为什么?
    ArrayList使用普通for循环效率更高,因为对于数组结构的数据来说,for循环采用的是下标访问;
    但是LinkedList是通过链表实现的,for循环时每获取第i个元素都必须从头开始遍历;foreach是通过iterator实现的遍历,而iterator遍历就是从头开始遍历,遍历完只需要一次;所以使用增强for循环效率更高。
    结论:for和foreach循环效率差不多,for循环稍微好一点;但是对于链表来说,for循环效率明显比foreach低
    *Java中有几种类型的流?分别是什么?
    读写操作,我们通常称为输入/输入(IO)操作。Java通过流进行IO操作的。
    字节流
    InputStream是所有字节输入流的祖先,
    OutputStream是所有字节输出流的祖先。
    字符流
    Reader是所有读取字符串输入流的祖先
    Writer是所有输出字符串的祖先

字节高效流:BufferedOutputStream 、BufferedInputStream
字符高效流:BufferedWriter、BufferedReader
转换流: OutputStreamWriter、InputStreamReader
什么是Java序列化,如何实现Java序列化?
java的序列化:
把Java对象转换为二进制数据流的过程。
实现:
需要序列化的类实现Serializable接口
序列化和反序列化的含义
1.把内存中的对象保存到一个文件中或者数据库中;
2.网络上传送对象;
3.通过RMI传输对象;
好处:
1.描述数据的传输格式,这样可以方便自己组织数据传输格式,以至于避免一些麻烦及错误
2.如果是跨平台的序列化,则发送方序列化后,接收方可以用任何其支持的平台反序列化成相应的版本,比如 Java序列化后, 用.net、phython等反序列化
transient关键字对序列化有影响吗
有,transient表示瞬态的,被transient关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化。
什么是线程?什么是多线程?
1)什么是线程:
在一个进程中,每个独立的功能都需要独立的去运行,这时又需要把当前这个进程划分成多个运行区域,每个独立的小区域(小单元)称为一个线程。
例如:360杀毒软件,同时既可以安全体检电脑又可以清理电脑中的垃圾。那么这里的安全体检是360杀毒软件中的一个线程,清理电脑中的垃圾也是一个线程。
2)什么是多线程:
一个进程如果只有一条执行任务,则称为单线程程序。
一个进程如果有多条执行任务,也就是说在一个进程中,同时开启多个线程,让多个线程同时去完成某些任务(功能)。则称为多线程程序。
实现多线程的方式?用哪个更好?
1.方式一:继承Thread,重写Thread类中的run方法;
2.方式二:实现Runnable接口,实现run方法;
3.方式二的方式更好,原因是:
①避免了Java单继承的局限性;
②把线程代码和任务的代码分离,解耦合(解除线程代码和任务的代码模块之间的依赖关系)。代码的扩展性非常好;
③方式二可以更方便、灵活的实现数据的共享
什么是线程安全问题
多线程环境中,且存在数据共享,一个线程访问的共享数据被其它线程修改了,那么就发生了线程安全问题;整个访问过程中,无一共享的数据被其他线程修改,就是线程安全的
如何解决线程安全问题
1.使用线程同步机制,使得在同一时间只能由一个线程修改共享数据;
2.消除共享数据:即多个线程数据不共享或者共享的数据不做修改。
使用局部变量,不使用成员变量。
如果使用成员变量,对成员变量不进行修改。
什么是线程同步?
当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态
启动一个线程是用run()还是start()? .
启动线程使用的是start()方法。
当用start()开始一个线程后,线程就进入就绪状态,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行。这并不意味着线程就会立即运行。当cpu分配给它时间时,才开始执行run()方法(如果有的话)。start()是方法,它调用run()方法.而run()方法是你必须重写的. run()方法中包含的是线程的主体
什么是死锁?死锁产生的原因有哪些?
1.什么是死锁
死锁是指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待的现象
2.死锁产生的原因
①系统资源的竞争
通常系统中拥有的不可剥夺资源,其数量不足以满足多个进程运行的需要,使得进程在 运行过程中,会因争夺资源而陷入僵局,如磁带机、打印机等。只有对不可剥夺资源的竞争 才可能产生死锁,对可剥夺资源的竞争是不会引起死锁的。
②进程推进顺序非法
进程在运行过程中,请求和释放资源的顺序不当,也同样会导致死锁。例如,并发进程 P1、P2分别保持了资源R1、R2,而进程P1申请资源R2,进程P2申请资源R1时,两者都 会因为所需资源被占用而阻塞
什么是线程同步?
当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态
启动一个线程是用run()还是start()? .
启动线程使用的是start()方法。
当用start()开始一个线程后,线程就进入就绪状态,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行。这并不意味着线程就会立即运行。当cpu分配给它时间时,才开始执行run()方法(如果有的话)。start()是方法,它调用run()方法.而run()方法是你必须重写的. run()方法中包含的是线程的主体
什么是死锁?死锁产生的原因有哪些?
1.什么是死锁
死锁是指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待的现象
2.死锁产生的原因
①系统资源的竞争
通常系统中拥有的不可剥夺资源,其数量不足以满足多个进程运行的需要,使得进程在 运行过程中,会因争夺资源而陷入僵局,如磁带机、打印机等。只有对不可剥夺资源的竞争 才可能产生死锁,对可剥夺资源的竞争是不会引起死锁的。
②进程推进顺序非法
进程在运行过程中,请求和释放资源的顺序不当,也同样会导致死锁。例如,并发进程 P1、P2分别保持了资源R1、R2,而进程P1申请资源R2,进程P2申请资源R1时,两者都 会因为所需资源被占用而阻塞
死锁产生的必要条件?如何解决死锁问题
死锁产生的条件
产生死锁必须同时满足以下四个条件,只要其中任一条件不成立,死锁就不会发生。
①互斥条件(Mutual exclusion):资源不能被共享,只能由一个进程使用。
②请求与保持条件(Hold and wait):进程已获得了一些资源,但因请求其它资源被阻塞时,对已获得的资源保持不放。
③不可抢占条件(No pre-emption) :有些系统资源是不可抢占的,当某个进程已获得这种资源后,系统不能强行收回,只能由进程使用完时自己释放。
④循环等待条件(Circular wait):若干个进程形成环形链,每个都占用对方申请的下一个资源。
如何解决死锁问题?
死锁解决几种方式:
①加锁顺序(线程按照一定的顺序加锁,只有获得了从顺序上排在前面的锁之后,才能获取后面的锁)
②加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
③死锁检测 (判断系统是否处于死锁状态)
④死锁避免(指进程在每次申请资源时判断这些操作是否安全。例如银行家算法:在分配资源之前先看清楚,资源分配后是否会导致系统死锁。如果会死锁,则不分配,否则就分配。)
sleep()和wait()的区别
1.sleep()方法,属于Thread类的。;wait()方法,则是属于Object类的;
2.sleep方法不会释放锁,而wait方法会释放锁
3.wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用
4.sleep需要接收时间参数,wait不需要接收时间参数;
5.sleep可以自然醒,wait必须等待别人唤醒;
notify和notifyAll
notify和notifyAll都可以唤醒处于等待的线程。
1.如果调用了线程 wait()方法,那么线程便会处于等待状态。
2.当调用notifyAll()方法后,将唤醒所有等待的线程
3.当调用notify()方法后,将随机唤醒一个等待线程
什么是锁机制?及其优缺点
有些业务逻辑在执行过程中要求对数据进行排他性的访问,于是需要通过一些机制保证在此过程中数据被锁住不会被外界修改,这就是所谓的锁机制。
优点:保证资源同步
缺点:有等待肯定会慢

乐观锁和悲观锁
悲观锁:总是假设最坏的情况。
每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁。
传统的关系型数据库里边就用到了很多这种锁机制,比如读锁,写锁等,都是在做操作之前先上锁。
乐观锁
总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据。
乐观锁适用于写比较少的情况,即冲突真的很少发生,这样可以省去锁的开销,从而提高系统的吞吐量。
悲观锁使用于写比较频繁的情况,即经常产生冲突,上层引用会不断的进行重试,这样反倒降低了性能,所以使用锁比较合适。

反射的原理及应用
原理:反射在程序运行时,能够动态的操作类的成员。
1.反射前提:通过字节码获得Class对象
2.反射操作:通过Class对象对构造Constructor、字段Field、方法Method进行动态操作。
3.反射应用:框架底层都使用了反射

手写单例设计模式(懒汉式、饿汉式-基于双重检查锁)**

1.饿汉式单例设计模式:
public class Singleton {
   //私有构造方法
   private Singleton() {}
   //单例对象
   private volatile static Singleton instance = new Singleton();  
   //静态工厂方法
   public static Singleton getInstance() {
        return instance;
    }
}

2.基于双重检查锁的懒汉式单例设计模式:
public class Singleton {
   //私有构造方法
   private Singleton() {}
   //单例对象
   private volatile static Singleton instance = null;  
   //静态工厂方法
   public static Singleton getInstance() {
        if (instance == null) {      //双重检测机制
         synchronized (Singleton.class){  //同步锁
           if (instance == null) {     //双重检测机制
             instance = new Singleton();
               }
            }
         }
        return instance;
    }
}

你可能感兴趣的:(基础)