Java 面试题

进程和线程区别

1、进程和线程切换时,进程的上下文切换时间开销远远大于线程上下文切换时间

2、进程的并发性较低,线程的并发性较高

3、进程有独立的地址空间,线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间

4、每个独立的进程有一个程序运行的入口、顺序执行序列和程序的出口

5、线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制

6、系统在运行的时候会为每个进程分配不同的内存空间;线程组之间只能共享进程资源

7、一个进程崩溃后,不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。

什么是多线程 

1、线程是程序执行的最小单元,一个进程可以拥有多个线程

2、各个线程之间共享程序的内存空间(代码段、数据段和堆空间)和系统分配的资源(CPU,I/O,打开的文件),各个线程拥有自己的栈空间

3、多线程优点:减少程序响应时间;提高CPU利用率;创建和切换开销小;数据共享效率高;

怎么保证线程安全 

1、线程安全问题是指在多线程背景下,线程没有按照我们的预期执行,导致操作共享变量出现异常

2、在Java中提供同步方案,从轻到重有三种方式:原子类、volatile关键字、锁

3、原子类是juc atomic包下的一系列类

4、volatile关键字是轻量级的同步机制,他实现了变量的可见性、防止指令重排序

5、原子类和volatile只能保证单个共享变量的线程安全,锁则可以保证临界区内的多个共享变量线程安全

6、java中常用的锁有两种:synchronized+juc包下的lock锁

7、synchronized锁是互斥锁,可以作用于实例方法、静态方法、代码块

8、lock锁接口可以通过lock、unlock方法锁住一段代码

9、除了锁以外,juc包下还提供了一些线程同步工具类,如CountDownLatch、Semaphore等等,我们还可以使用ThreadLocal定义线程局部变量

java中有哪些集合

List:ArrayList、LinkedList

Set:HashSet

Map:HashMap、HashTable、ConcurrentHashMap

Serializable 接口

  1. 一个对象想要被序列化,那么它的类就要实现此接口或者它的子接口。
  2. 这个对象的所有属性(包括private属性、包括其引用的对象)都可以被序列化和反序列化来保存、传递。
  3. 凡是被static修饰的字段是不会被序列化的。
  4. 凡是被transient修饰符修饰的字段也是不会被序列化的。
  5. 序列化:把Java对象转换为字节序列。
  6. 反序列化:把字节序列恢复为原先的Java对象。

serialVersionUID号有何用?

  1. 在反序列化时,JVM会把字节流中的序列号ID和被序列化类中的序列号ID做比对,只有两者一致,才能重新反序列化,否则就会报异常来终止反序列化的过程。
  2. 没有显式地给它定义一个serialVersionUID的话,则Java运行时环境会根据该类的各方面信息自动地为它生成一个默认的serialVersionUID,一旦像上面一样更改了类的结构或者信息,则类的serialVersionUID也会跟着变化!

JDK ,JRE ,JVM 区别

  • JDK(Java SE Development Kit),Java 标准开发包,它提供了编译,运行java程序所需的各种工具和资源,包括java编译器,java运行时环境,以及常用的java类库
  • JRE(Java Runtime EnVironment),java运行环境,用于运行java的字节码文件,JRE中包括了JVM以及JVM所需的类库,普通用户而只需要安装JRE来运行Java程序,而程序开发者必须安装JDK来编译,调试程序。
  • JVM(Java Virtual Mechinal),Java虚拟机,是JRE的一部分,它是整个java实现跨平台的最核心部分,负责运行字节码文件

Java8新特性

Lambada 表达式

方法引用、构造器引用

Stream API

为什么Java是编译解释共存

首先javac编译成字节码文件,然后JIT解释成机器码。 

JIT(just-in-time compilation) 编译器。即时编译

当 JIT 编译器完成第一次编译后,其会将字节码对应的机器码保存下来,下次可以直接使用。

静态方法为什么不能调用非静态成员?

静态方法是属于类的,在类加载的时候就会分配内存,可以通过类名直接访问。而非静态成员属于实例对象,只有在对象实例化之后才存在,需要通过类的实例对象去访问。

什么是可变参数?

以接受 0 个或者多个参数,只能作为函数的最后一个参数。

遇到重载会优先选用固定长度参数

包装类型缓存机制?

Byte,Short,Integer,Long 这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据

Character 创建了数值在 [0,127] 范围的缓存数据,Boolean 直接返回 True or False

Float,Double 并没有实现缓存机制。

Integer i1 = 40;
Integer i2 = new Integer(40);
System.out.println(i1==i2);

Integer i1=40 这一行代码会发生装箱,也就是说这行代码等价于 Integer i1=Integer.valueOf(40) 。因此,i1 直接使用的是缓存中的对象。而Integer i2 = new Integer(40) 会直接创建新的对象。答案是 false。

为什么浮点数会有精度丢失的风险?

计算机在表示一个数字时,宽度是有限的,无限循环的小数存储在计算机时,只能被截断,所以就会导致小数精度发生损失的情况。

BigDecimal 可以实现对浮点数的运算,不会造成精度丢失。

超过long整型数据应该如何表示?

BigInteger 内部使用 int[] 数组来存储任意大小的整形数据。

为什么重写equal() 必须重写hashCode()?

  • equals 方法判断两个对象是相等的,那这两个对象的 hashCode 值也要相等。
  • 两个对象有相同的 hashCode 值,他们也不一定是相等的(哈希碰撞)。

String s1 = new String("abc") 创建了几个对象?

会创建 1 或 2 个字符串对象。

intern 方法作用

  • 如果字符串常量池中保存了对应的字符串对象的引用,就直接返回该引用。
  • 如果字符串常量池中没有保存了对应的字符串对象的引用,那就在常量池中创建一个指向该字符串对象的引用并返回

常量折叠(Constant Folding) 的代码优化

String str3 = "str" + "ing"; 编译器会给你优化成 String str3 = "string"; 。

异常

  • Exception :程序本身可以处理的异常,可以通过 catch 来进行捕获。Exception 又可以分为 Checked Exception (受检查异常,必须处理) 和 Unchecked Exception (不受检查异常,可以不处理)。
  • Error :Error 属于程序无法处理的错误 ,不建议通过catch捕获 。例如 Java 虚拟机运行错误(Virtual MachineError)、虚拟机内存不够错误(OutOfMemoryError)、类定义错误(NoClassDefFoundError)等 。这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止。

Checked Exception 和 Unchecked Exception 有什么区别?

Checked Exception 即 受检查异常 ,Java 代码在编译过程中,如果受检查异常没有被 catch或者throws 关键字处理的话,就没办法通过编译。

Unchecked Exception 即 不受检查异常 ,Java 代码在编译过程中 ,我们即使不处理不受检查异常也可以正常通过编译。

RuntimeException 及其子类都统称为非受检查异常

finally 语句一定会被执行吗?

虚拟机被终止运行的话,finally 中的代码就不会被执行。

程序所在的线程死亡。

关闭 CPU。

什么是泛型?有什么作用?

Java 泛型(Generics) 是 JDK 5 中引入的一个新特性。使用泛型参数,可以增强代码的可读性以及稳定性。

泛型有哪几种?

泛型类、泛型接口、泛型方法

项目中哪里用到了泛型?

自定义接口通用返回结果 CommonResult<T>

构建集合工具类

什么是反射?

运行时分析类以及执行类中方法。通过反射你可以获取任意一个类的所有属性和方法,你还可以调用这些方法和属性。

反射可以让我们的代码更加灵活、为各种框架提供开箱即用的功能提供了便利。

安全问题

反射的性能稍差点

序列化和反序列化

如果我们需要持久化 Java 对象比如将 Java 对象保存在文件中,或者在网络传输 Java 对象,这些场景都需要用到序列化。

  • 序列化: 将数据结构或对象转换成二进制字节流的过程
  • 反序列化:将在序列化过程中所生成的二进制字节流转换成数据结构或者对象的过程

序列化的主要目的是通过网络传输对象或者说是将对象存储到文件系统、数据库、内存中。

不想进行序列化的变量,使用 transient 关键字修饰。

I/O 流为什么要分为字节流和字符流呢?

如果我们不知道编码类型的话,使用字节流的过程中很容易出现乱码问题

Java IO 中的设计模式有哪些?

适配器模式

装饰器模式

BIO、NIO 和 AIO 的区别?

BIO (Blocking I/O) 阻塞IO

NIO(Non-blocking/New I/O)非阻塞IO,多路复用

AIO(Asynchronous I/O)异步IO

语法糖

方便程序员开发程序而设计的一种特殊语法

常用的语法糖:泛型、变长参数、增强 for 循环、try-with-resources 语法、lambda 表达式

为什么java只有值传递?

java传递给方法的方式是值传递:

        参数是基本类型的话,是值的拷贝,会创建副本。

        参数是引用类型的话,是引用地址的拷贝,也会创建副本。

ArrayList 和 Vector 的区别?

ArrayList 线程不安全、Vector线程安全

ArrayList 与 LinkedList 结构?

ArrayList 底层使用的是 Object 数组

LinkedList 底层使用的是 双向链表 数据结构。

LinkedList 只有在头节点和尾节点插入复杂度为O(1),其他情况增删元素的时间复杂度都是 O(n) 

HashMap 和 Hashtable 的区别

线程是否安全:HashMap 是非线程安全的,Hashtable 是线程安全的

效率: 因为线程安全的问题,HashMap 要比 Hashtable 效率高一点

对 Null key 和 Null value 的支持: HashMap 可以存储 null 的 key 和 value,但 null 作为键只能有一个,null 作为值可以有多个;Hashtable 不允许有 null 键和 null 值,否则会抛出 NullPointerException

初始容量大小和每次扩充容量大小的不同:

Hashtable 默认的初始大小为 11,之后每次扩充,容量变为原来的 2n+1

HashMap 默认的初始化大小为 16。之后每次扩充,容量变为原来的 2 倍

创建时如果给定了容量初始值,那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为 2 的幂次方大小

底层数据结构:JDK1.8 以前 hash表+链表,以后hash表+链表+红黑树(链表长度阈值8)

Map 接口的实现

HashMap:

        无序

        允许null值和null键

        存储结构:数组+链表+红黑树

        初始长度为16

        当链表长度大于8时,且数组长度大于64时链表会自动转换为红黑树

        扩容阈值条件:长度 * 0.75

        每次扩容长度:长度 * 2

LinkedHashMap:

        有序的

        存储结构:数组+链表+红黑树

        有一个维护使用链表用来记录顺序

TreeMap

        存储结构:红黑树

HashMap的线程不安全

  1. 在JDK1.7中,当并发执行扩容操作时会造成环形链和数据丢失的情况。
  2. 在JDK1.8中,在并发执行put操作时会发生数据覆盖的情况。

ArrayList 扩容机制

new ArrayList() 长度为零

add(Object o)首次扩容为10,再次扩容为上次容量的1.5倍

addAll(Collection c) 扩容为C集合元素个数和原容器1.5倍中的最大值

fail-fast 与 fail-safe

ArrayList 是 fail-fast的典型代表,遍历同时不能修改,尽快失败

CopyOnWriteArrayList 是 fail-safe的典型代表,遍历的同时可以修改,原理是读写分离

ArrayList 与 LinkedList

ArrayList: 

  • 基于数组,需要连续内存
  • 随机访问快
  • 尾部插入、删除性能可以
  • 可以利用CPU缓存,局部性原理

LinkedList:

  • 基于双向链表,无需连续内存
  • 随机访问慢
  • 头尾插入删除性能高
  • 占用内存多

HashMap

1.7 与 1.8 有何不同?

        1.7 数组 + 链表  ,1.8 数组 +(链表|红黑树)

为何要用红黑树?

        避免DoS攻击,防止链表超长时性能下降

为何一上来不树化?

        TreeNode占用空间比普通Node的大

树化阈值为何是8?

        选择8是为了让树化几率足够小

何时会树化?

        链表长度超过树化阈值;数组容量>=64

何时退化位链表?

        在扩容时如果拆分树时,树节点个数<=6则退化为链表;

        remove树节点时,若root、root.left、root.right、root.left.left有一个为null,也会退化为链表

索引如何计算?

        计算对象的hashCode().再进行调用HashMap 的 hash() 方法进行二次哈希,最后&(capacity-1)得到索引

HashCode都有了,为何还要提供hash()方法?

        二次hash是为了综合高位数据,让哈希分布更为均匀

数组容量为何是2的N次幂?

        计算索引时,如果2的n次幂可以使用位与运算代替取模,效率更高;

        扩容时 hash&oldCap == 0 的元素,否则新位置 = 旧位置 + oldCap;

        采用2的n次幂有更高效率,采用质数有更好的hash分布。

put 1.7 和 1.8 不同

  •  链表插入节点时,1.7是头插法,1.8是尾插法
  • 1.7是大于等于阈值且没有空位时才扩容,而1.8是大于阈值就扩容
  • 1.8在扩容计算Node索引时,会优化

加载因子为何默认时0.75f

        在空间占用与查询时间之间取得较好的权衡

多线程下会有啥问题

        扩容死链(1.7)

        数据错乱(1.7,1.8)

Key能否为null,作为key的对象有什么要求?

        HashMap 的可以可以为null,但Map的其他实现则不然

        作为key对象,必须实现 hashCode 和 equals ,并且 key 的内容不能修改

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值