java基础面试

Posted 一支会记忆的笔

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java基础面试相关的知识,希望对你有一定的参考价值。

 

1)a = a + b 与 a += b 的区别

    += 隐式的将加操作的结果类型强制转换为持有结果的类型。如果两这个整型相加,如 byte、short 或者 int,首先会将它们提升到 int 类型,然后在执行加法操作

                        

  (因为 a+b 操作会将 a、b 提升为 int 类型,所以将 int 类型赋值给 byte 就会编译出错)

 2)3*0.1 == 0.3 将会返回什么?true 还是 false? 

    false,因为有些浮点数不能完全精确的表示出来

3)“a==b”和”a.equals(b)”有什么区别?

    如果 a 和 b 都是对象:则 a==b 是比较两个对象的引用,只有当 a 和 b 指向的是堆中的同一个对象才会返回 true,

    如果 a 和 b 都是数值型: 比较的则是数值是否相等

       a.equals(b) :是进行逻辑比较(内容的比较),所以通常需要重写该方法来提供逻辑一致性的比较。例如,String 类重写 equals() 方法,所以可以用于两个不同对象,但是包含的字母相同的比较。

== 和 equals 的区别是什么?   

  == 解读

    对于基本类型和引用类型 == 的作用效果是不同的,如下所示:

      • 基本类型:比较的是值是否相同;
      • 引用类型:比较的是引用是否相同;
String x = "string";
String y = "string";
String z = new String("string");
System.out.println(x==y); // true
System.out.println(x==z); // false
System.out.println(x.equals(y)); // true
System.out.println(x.equals(z)); // true 

    代码解读:因为 x 和 y 指向的是同一个引用,所以 == 也是 true,而 new String()方法则重写开辟了内存空间,所以 == 结果为 false,而 equals 比较的一直是值,所以结果都为 true。

  equals 解读(内容的比较)

    equals 本质上就是 ==,只不过 String 和 Integer 等重写了 equals 方法,把它变成了值比较
    首先来看默认情况下 equals 比较一个有相同值的对象,代码如下

class Cat {
    public Cat(String name) {
        this.name = name;
    }
 
    private String name;
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
}
 
Cat c1 = new Cat("王磊");
Cat c2 = new Cat("王磊");
System.out.println(c1.equals(c2)); // false
View Code

输出结果出乎我们的意料,竟然是 false?这是怎么回事,看了 equals 源码就知道了,源码如下:

public boolean equals(Object obj) {
    return (this == obj);
}

原来 equals 本质上就是 ==。

那问题来了,两个相同值的 String 对象,为什么返回的是 true?代码如下:

String s1 = new String("老王");
String s2 = new String("老王");
System.out.println(s1.equals(s2)); // true

同样的,当我们进入 String 的 equals 方法,找到了答案,代码如下:

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}
View Code

  原来是 String 重写了 Object 的 equals 方法,把引用比较改成了值比较。

  总结 :== 对于基本类型来说是值比较,对于引用类型来说是比较的是引用;而 equals 默认情况下是引用比较,只是很多类重新了 equals 方法,比如 String、Integer 等把它变成了值比较,所以一般情况下 equals 比较的是值是否相等。

两个对象的 hashCode()相同,则 equals()也一定为 true,对吗?

不对,两个对象的 hashCode()相同,equals()不一定 true。

代码示例:

String str1 = "通话";
String str2 = "重地";
System.out.println(String.format("str1:%d | str2:%d",  str1.hashCode(),str2.hashCode()));
System.out.println(str1.equals(str2));

执行结果:

str1:1179395 | str2:1179395

false

 

4)final、finalize 和 finally 的不同之处?

   final 是一个修饰符,可以修饰变量、方法和类

      如果 final 修饰变量,意味着该变量的值在始化后不能被改变

      如果final 饰类: 则该类不能被继承 意味着它不能再派生出新的子类

      被final修饰的方法:也同样只能使用,不能覆盖(重写)。  

  finally 是一个关键字,与 try 和 catch 一起用于异常的处理。finally 块一定会被执行,无论在 try 块中是否有发生异常。

    finalize() :这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的方法

. final 在 java 中有什么作用?

  • final 修饰的类叫最终类,该类不能被继承。
  • final 修饰的方法不能被重写。
  • final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。

 5)ArrayList 与 LinkedList 的不区别?

     ArrrayList 底层的数据结构是数组支持随机访问,ArrayList 的时间复杂度是 O(1)

    LinkedList 的底层数据结构书链表,不支持随机访问。使用下标访问一个元素, LinkedList 的时间复杂度是 O(n)。

 6)面向对象的特征有哪些方面?

  1)抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。

  2)继承:继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类(超类、基类);得到继承信息的类被称为子类(派生类)。继承让变化中的软件系统有了一定的延续性,同时继承也是封装程序中可变因素的重要手段(如果不能理解请阅读阎宏博士的《Java与模式》或《设计模式精解》中关于桥梁模式的部分)。

  3)封装:通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。面向对象的本质就是将现实世界描绘成一系列完全自治、封闭的对象。我们在类中编写的方法就是对实现细节的一种封装;我们编写一个类就是对数据和数据操作的封装。可以说,封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程接口(可以想想普通洗衣机和全自动洗衣机的差别,明显全自动洗衣机封装更好因此操作起来更简单;我们现在使用的智能手机也是封装得足够好的,因为几个按键就搞定了所有的事情)。

  4)多态性:多态性是指允许不同子类型的对象对同一消息作出不同的响应。简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情。多态性分为编译时的多态性和运行时的多态性。如果将对象的方法视为对象向外界提供的服务,那么运行时的多态性可以解释为:当A系统访问B系统提供的服务时,B系统有多种提供服务的方式,但一切对A系统来说都是透明的(就像电动剃须刀是A系统,它的供电系统是B系统,B系统可以使用电池供电或者用交流电,甚至还有可能是太阳能,A系统只会通过B类对象调用供电的方法,但并不知道供电系统的底层实现是什么,究竟通过何种方式获得了动力)。

    方法重载(overload)实现的是编译时的多态性(也称为前绑定)

    方法重写(override)实现的是运行时的多态性(也称为后绑定)。运行时的多态是面向对象最精髓的东西

    要实现多态需要做两件事

  1. 方法重写(子类继承父类并重写父类中已有的或抽象的方法);

  2. 对象造型(用父类型引用引用子类型对象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)。

 7)short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗?

    对于short s1 = 1; s1 = s1 + 1;由于1是int类型,因此s1+1运算结果也是int 型,需要强制转换类型才能赋值给short型。

    short s1 = 1; s1 += 1;可以正确编译,因为s1+= 1;相当于s1 = (short)(s1 + 1);其中有隐含的强制类型转换。

8)&和&&的区别?  

  &运算符有两种用法:(1)按位与;(2)逻辑与

  &&运算符是短路与运算

  逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是true整个表达式的值才是true。&&之所以称为短路运算是因为,如果&&左边的表达式的值是false,右边的表达式会被直接短路掉,不会进行运算。很多时候我们可能都需要用&&而不是&,例如在验证用户登录时判定用户名不是null而且不是空字符串,应当写为:username != null &&!username.equals(“”),二者的顺序不能交换,更不能用&运算符,因为第一个条件如果不成立,根本不能进行字符串的equals比较,否则会产生NullPointerException异常。注意:逻辑或运算符(|)和短路或运算符(||)的差别也是如此

 2 << 3(左移3位相当于乘以2的3次方,右移3位相当于除以2的3次方)

9)抽象类(abstract class)和接口(interface)有什么异同?

  抽象类和接口都不能够实例化但可以定义抽象类和接口类型的引用

  相同点:

    一个类如果继承了某个抽象类或者实现了某个接口都需要对其中的抽象方法全部进行实现否则该类仍然需要被声明为抽象类。接口比抽象类更加抽象,

  不同点:

    抽象类  中可以定义构造器可以有抽象方法和具体方法

      抽象类 中的成员可以是private、默认、protected、public的

                抽象类 可以定义成员变量 有抽象方法的类必须被声明为抽象类,而抽象类未必要有抽象方法

    接口中 能定义构造器而且其中的方法全部都是抽象方法

      接口中 的成员全都是public的

                接口中 定义的成员变量实际上都是常量

普通类和抽象类有哪些区别?

  • 普通类不能含抽象方法,抽象类可以包含抽象方法。
  • 抽象类不能直接实例化,普通类可以直接实例化。

抽象类能使用 final 修饰吗?

  不能,定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承,这样彼此就会产生矛盾,所以 final 不能修饰抽象类

接口和抽象类有什么区别?

  实现:抽象类的子类使用 extends 来继承;接口必须使用 implements 来实现接口。
  构造函数:抽象类可以有构造函数;接口不能有。
  main 方法:抽象类可以有 main 方法,并且我们能运行它;接口不能有 main 方法。
  实现数量:类可以实现很多个接口;但是只能继承一个抽象类。
  访问修饰符:接口中的方法默认使用 public 修饰;抽象类中的方法可以是任意访问修饰符。

10)String s=new String(“xyz”);创建了几个字符串对象?

  两个对象,一个是静态存储区的"xyz",一个是用new创建在堆上的对象。

11)阐述Spring框架中Bean的生命周期? 


    Spring IoC容器找到关于Bean的定义并实例化该Bean。
    Spring IoC容器对Bean进行依赖注入
    如果Bean实现了BeanNameAware接口,则将该Bean的id传给setBeanName方法
  ④  如果Bean实现了BeanFactoryAware接口,则将BeanFactory对象传给setBeanFactory方法。
  ⑤  如果Bean实现了BeanPostProcessor接口,则调用其postProcessBeforeInitialization方法。
  ⑥  如果Bean实现了InitializingBean接口,则调用其afterPropertySet方法。
  ⑦  如果有和Bean关联的BeanPostProcessors对象,则这些对象的postProcessAfterInitialization方法被调用。
  ⑧  当销毁Bean实例时,如果Bean实现了DisposableBean接口,则调用其destroy方法。

12)Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?

  启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解

  @SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。

  @EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能: @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。

  @ComponentScan:Spring组件扫描。

13)object中定义了哪些方法?

  clone(), equals(), hashCode(), toString(), notify(), notifyAll(), wait(), finalize(), getClass()

13)进程间通信有哪几种方式?

  1)管道(Pipe),2)命名管道(named pipe),3)信号(Signal),4)消息(Message)队列,5)共享内存,6)内存映射(mapped memory),7)信号量(semaphore),8)套接口(Socket)

14)String 属于基础的数据类型吗?

String 不属于基础类型基础类型有 8 种:byte、boolean、char、short、int、float、long、double,而 String 属于对象

            

15)java 中操作字符串都有哪些类?它们之间有什么区别?

操作字符串的类有:StringStringBufferStringBuilder

  String 和 StringBuffer、StringBuilder 的区别

   String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象

     StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。

  StringBuffer 和 StringBuilder 最大的区别:

  StringBuffer 是线程安全的,而 StringBuilder非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。

16)String str="i"与 String str=new String("i")一样吗?

  不一样,因为内存的分配方式不一样。String str="i"的方式,java 虚拟机会将其分配到常量池中;而 String str=new String("i") 则会被分到堆内存中。

17)如何将字符串反转?

  使用 StringBuilder 或者 stringBufferreverse() 方法。

// StringBuffer reverse
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("abcdefg");
System.out.println(stringBuffer.reverse()); // gfedcba
// StringBuilder reverse
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("abcdefg");
System.out.println(stringBuilder.reverse()); // gfedcba

18)String 类的常用方法都有那些?

  indexOf():返回指定字符的索引。
  charAt():返回指定索引处的字符。
  replace():字符串替换。
  trim():去除字符串两端空白。
  split():分割字符串,返回一个分割后的字符串数组。
  getBytes():返回字符串的 byte 类型数组。
  length():返回字符串长度。
  toLowerCase():将字符串转成小写字母。
  toUpperCase():将字符串转成大写字符。
  substring():截取字符串。
  equals():字符串比较。

19) java 中 IO 流分为几种?

  按功能来分:输入流(input)、输出流(output)。

  按类型来分:字节流和字符流。

  字节流和字符流的区别是:字节流按 8 位传输以字节为单位输入输出数据,字符流按 16 位传输以字符为单位输入输出数据。

20)BIO、NIO、AIO 有什么区别?

  BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。
  NIO:New IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。
  AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。

21)Files的常用方法都有哪些?

  Files.exists():检测文件路径是否存在。
  Files.createFile():创建文件。
  Files.createDirectory():创建文件夹。
  Files.delete():删除一个文件或目录。
  Files.copy():复制文件。
  Files.move():移动文件。
  Files.size():查看文件个数。
  Files.read():读取文件。
  Files.write():写入文件。

22)HashMap 和 Hashtable 有什么区别?

  hashMap去掉了HashTable 的contains方法,但是加上了containsValue()和containsKey()方法。
  hashTable同步的,而HashMap是非同步的,效率上逼hashTable要高。
  hashMap允许空键值,而hashTable不允许。

23)说一下 HashMap 的实现原理?

  HashMap概述: HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。 

  HashMap的数据结构: 在java编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的,HashMap也不例外。HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。

  当我们往Hashmap中put元素时,首先根据key的hashcode重新计算hash值,根绝hash值得到这个元素在数组中的位置(下标),如果该数组在该位置上已经存放了其他元素,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放入链尾.如果数组中该位置没有元素,就直接将该元素放到数组的该位置上。

24)ArrayList 和 LinkedList 的区别是什么?

  区别是 ArrrayList底层的数据结构是数组支持随机访问,而 LinkedList 的底层数据结构是双向循环链表,不支持随机访问。使用下标访问一个元素,ArrayList 的时间复杂度O(1),而 LinkedList 是 O(n)。

25)如何实现数组和 List 之间的转换?

  • List转换成为数组:调用ArrayList的toArray方法。
  • 数组转换成为List:调用Arrays的asList

多线程

26)并行和并发有什么区别?

  并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔发生。
  并行是在不同实体上的多个事件,并发是在同一实体上的多个事件。
  在一台处理器上“同时”处理多个任务,在多台处理器上同时处理多个任务。如hadoop分布式集群。
所以并发编程的目标是充分的利用处理器的每一个核,以达到最高的处理性能。

27)创建线程有哪几种方式?

  ①. 继承Thread类创建线程类

    定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。
    创建Thread子类的实例,即创建了线程对象。
    调用线程对象的start()方法来启动该线程。
  ②. 通过Runnable接口创建线程类

    定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
    创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
    调用线程对象的start()方法来启动该线程。
  ③. 通过Callable和Future创建线程

    创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
    创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
    使用FutureTask对象作为Thread对象的target创建并启动新线程。
    调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。

28)说一下 runnable 和 callable 有什么区别?

  有点深的问题了,也看出一个Java程序员学习知识的广度。

  Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已;
  Callable接口中的call()方法是有返回值的是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果

29)线程有哪些状态?

  线程通常都有五种状态,创建、就绪、运行、阻塞和死亡

    创建状态。在生成线程对象new 线程时,并没有调用该对象的start方法,这是线程处于创建状态。
    就绪状态。当调用了线程对象的start方法之后,该线程就进入了就绪状态,但是此时线程调度程序还没有把该线程设置为当前线程,此时处于就绪状态。在线程运行之后,从等待或者睡眠中回来之后,也会处于就绪状态。
    运行状态。线程调度程序将处于就绪状态的线程设置为当前线程(获得CPU时),此时线程就进入了运行状态,开始运行run函数当中的代码。
    阻塞状态。线程正在运行的时候,被暂停,通常是为了等待某个时间的发生(比如说某项资源就绪)之后再继续运行。sleep,suspend,wait等方法都可以导致线程阻塞。
    死亡状态。如果一个线程的run方法执行结束或者调用stop方法后,该线程就会死亡。对于已经死亡的线程,无法再使用start方法令其进入就绪

30)sleep() 和 wait() 有什么区别?

  sleep():方法是线程类(Thread)的静态方法,让调用线程进入睡眠状态,让出执行机会给其他线程,等到休眠时间结束后,线程进入就绪状态和其他线程一起竞争cpu的执行时间。(等到一定时间自动唤醒)

      因为sleep() 是static静态的方法,他不能改变对象的机锁,当一个synchronized块中调用了sleep() 方法,线程虽然进入休眠,但是对象的机锁(cpu)没有被释放其他线程依然无法访问这个对象。

  wait():wait()是Object类的方法,当一个线程执行到wait方法时,它就进入到一个和该对象相关的等待池,同时释放对象的,使得其他线程能够访问,可以通过notify,notifyAll方法来唤醒等待的线程。

31)notify()和 notifyAll()有什么区别?

  如果线程调用了对象的 wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁
  当有线程调用了对象的 notifyAll()方法唤醒所有 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。

  也就是说,调用了notify后只要一个线程会由等待池进入锁池,而notifyAll会将该对象等待池内的所有线程移动到锁池中,等待锁竞争。
  优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用 wait()方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了 synchronized 代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁

32)线程的 run()和 start()有什么区别?

  每个线程都是通过某个特定Thread对象所对应的方法run()来完成其操作的,方法run()称为线程体通过调用Thread类的start()方法来启动一个线程

  start()方法来启动一个线程,真正实现了多线程运行。这时无需等待run方法体代码执行完毕可以直接继续执行下面的代码; 这时此线程是处于就绪状态, 并没有运行。 然后通过此Thread类调用方法run()来完成其运行状态, 这里方法run()称为线程体,它包含了要执行的这个线程的内容, Run方法运行结束, 此线程终止。然后CPU再调度其它线程。

  run()方法是在本线程里的只是线程里的一个函数,而不是多线程的。 如果直接调用run(),其实就相当于是调用了一个普通函数而已,直接待用run()方法必须等待run()方法执行完毕才能执行下面的代码,所以执行路径还是只有一条,根本就没有线程的特征,所以在多线程执行时要使用start()方法而不是run()方法。

33)创建线程池有哪几种方式?

①. newFixedThreadPool(int nThreads)

创建一个固定长度的线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程规模将不再变化,当线程发生未预期的错误而结束时,线程池会补充一个新的线程。

②. newCachedThreadPool()

创建一个可缓存的线程池,如果线程池的规模超过了处理需求,将自动回收空闲线程,而当需求增加时,则可以自动添加新线程,线程池的规模不存在任何限制。

③. newSingleThreadExecutor()

这是一个单线程的Executor,它创建单个工作线程来执行任务,如果这个线程异常结束,会创建一个新的来替代它;它的特点是能确保依照任务在队列中的顺序来串行执行

④. newScheduledThreadPool(int corePoolSize)

创建了一个固定长度的线程池,而且以延迟或定时的方式来执行任务,类似于Timer。

34)线程池中 submit()和 execute()方法有什么区别?

  接收的参数不一样
  submit有返回值,而execute没有
  submit方便Exception处理

35) 在 java 程序中怎么保证多线程的运行安全?

  线程安全在三个方面体现:

    原子性:提供互斥访问,同一时刻只能有一个线程对数据进行操作,(atomic,synchronized);
    可见性:一个线程对主内存的修改可以及时地被其他线程看到,(synchronized,volatile);
    有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序,该观察结果一般杂乱无序,(happens-before原则)。

49)什么是死锁?

  死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称死锁进程。是操作系统层面的一个错误,是进程死锁的简称,最早在 1965 年由 Dijkstra 在研究银行家算法时提出的,它是计算机操作系统乃至整个并发程序设计领域最难处理的问题之一。

50)怎么防止死锁?

  死锁的四个必要条件

    互斥条件:进程对所分配到的资源不允许其他进程进行访问,若其他进程访问该资源,只能等待,直至占有该资源的进程使用完成后释放该资源
    请求和保持条件:进程获得一定的资源之后,又对其他资源发出请求,但是该资源可能被其他进程占有,此事请求阻塞,但又对自己获得的资源保持不放
    不可剥夺条件:是指进程已获得的资源,在未完成使用之前,不可被剥夺,只能在使用完后自己释放
    环路等待条件:是指进程发生死锁后,若干进程之间形成一种头尾相接的循环等待资源关系
    这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之 一不满足,就不会发生死锁

51)说一下 synchronized 底层实现原理?

  synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性

  Java中每一个对象都可以作为锁,这是synchronized实现同步的基础:

  普通同步方法,锁是当前实例对象
  静态同步方法,锁是当前类的class对象
  同步方法块,锁是括号里面的对象

52)synchronized 和 volatile 的区别是什么?

  volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
  volatile仅能使用在变量级别synchronized则可以使用在变量、方法、和类级别的。
  volatile仅能实现变量的修改可见性不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性
  volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
  volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化。

53)synchronized 和 Lock 有什么区别?

  首先synchronizedjava内置关键字,在jvm层面,Lock是个java类
  synchronized无法判断否获取锁的状态Lock可以判断是否获取到锁;
  synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;
  用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;
  synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可);

Java Web

54) jsp 和 servlet 有什么区别?

  jsp经编译后就变成了Servlet.(JSP的本质就是ServletJVM只能识别java的类,不能识别JSP的代码Web容器将JSP的代码编译成JVM能够识别的java类
  jsp更擅长表现于页面显示servlet更擅长于逻辑控制
  Servlet中没有内置对象Jsp中的内置对象都是必须通过HttpServletRequest对象,HttpServletResponse对象以及HttpServlet对象得到
  Jsp是Servlet的一种简化,使用Jsp只需要完成程序员需要输出到客户端的内容,Jsp中的Java脚本如何镶嵌到一个类中,由Jsp容器完成。而Servlet则是个完整的Java类,这个类的Service方法用于生成对客户端的响应。

55) jsp 有哪些内置对象?作用分别是什么?

JSP有9个内置对象:

  request:封装客户端的请求,其中包含来自GET或POST请求的参数;
  response:封装服务器对客户端的响应
  pageContext:通过该对象可以获取其他对象
  session:封装用户会话的对象
  application:封装服务器运行环境的对象;
  out:输出服务器响应的输出流对象;
  config:Web应用的配置对象
  page:JSP页面本身(相当于Java程序中的this);
  exception:封装页面抛出异常的对象。

56) 说一下 jsp 的 4 种作用域?

JSP中的四种作用域包括page、request、session和application,具体来说:

  page代表与一个页面相关的对象和属性
  request代表与Web客户端发出的一个请求相关的对象和属性。一个请求可能跨越多个页面,涉及多个Web组件;需要在页面显示的临时数据可以置于此作用域。
  session代表与某个用户与服务器建立的一次会话相关的对象和属性。跟某个用户相关的数据应该放在用户自己的session中。
  application代表与整个Web应用程序相关的对象和属性,它实质上是跨越整个Web应用程序,包括多个页面、请求和会话的一个全局作用域。

57)session 和 cookie 有什么区别?

由于HTTP协议是无状态的协议,以上是关于java基础面试的主要内容,如果未能解决你的问题,请参考以下文章

面试常用的代码片段

面向面试编程代码片段之GC

经验总结:Java高级工程师面试题-字节跳动,成功跳槽阿里!

JAVA基础面试题

Java语言基础之方法的设计

Java面试题Java基础