java面试题

Java基础

1、面向对象?(10K)

面向对向对象更易复用、扩展、维护。注重每一个步骤、顺序。
封装:封装的意义,在于明确标识出允许外部使用的所有成员函数数据项 例子:JavaBen 封装的一些框架直接来使用。
继承 :继承基类的方法,并做出自己的改变和扩展 子类直接复用父类的属性 方法。
多态:基于对象所属类的不同,外部对同一个的方法的调用,实际执行逻辑值不同。

面向过程 性能直接比较高效 注重对象具体做什么

2、==和equals比较 (10K)

==对比是栈中的值,用**** :基本数据类型比较的内容是变量值,引用类型的值比较的是对象的地址
equals:object默认也是采用
比较,通常会重写。
string已经重写了equals的方法,比较的是字符的类容。

3、final的作用 (10K)
  • 修饰类:表示不可以被继承
  • 修饰方法:表示方法不可以被子类覆盖,但可以重载
  • 修饰变量:表示变量一旦被赋值就不可以更改它的值
    (1)修饰成员变量
  • 修饰的类变量 ,只能在静态初始化块中指定初始化值或申明该类变量指定的初始值。
  • 饰成员变量:可以在非静态初始化块、申明该变量或者构造器中执行初始值。
  • 修饰的局部成员变量在使用时 可以在赋值。(局部变量只声明没有初始化,不会报错,与fina1无关。

    (3)修饰基本类型数据和引用类型数据
  • 如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;
  • 如果是引用类型的变量,则在对其初始化之后便不能再让其指向另于个对象。但是引用的值是可变的。
4、为什么局部内部类和匿名内部类只能访问局部final变量? (10K)
5、ArrayList和LinkedList的区别?(10K)
  • ArrayList是基于索引的数据结构,底层是数组基于动态数组,连续内存存储,适合下标访问(随机访问),扩容机制:因为数组长度固定,超出长度存数据时需要新建数组,然后将老数组的数据拷贝到新数组,如果不是尾部插入数据还会涉及到元素的移动(往
    后复制一份,插入新元索),使用尾插法并指定初始容量可以极大提升性能、甚至超过linkedList (需要创建大量的node对象)

  • LinkedList是以元素列表存储的数据,链表 每一个元素都和他前后元素链接在一起的,它查找下一个元素的时间复杂度是O(n)。LinkedList: 基于链表,可以存储在分散的内存中,适合做数据插入及删除操作,**不适合查询:需要逐一遍历,**遍历LinkedList必须使用iterator不能使用for循环,因为每次for循环体内通过get(i)取得某一 元素时都需要对list重新进行遍历,性能消耗极大。另外不要试图使用indexOf等返回元素索引,并利用其进行遍历,使用indexIOf对list进行了遍历,当结果为空时会遍历整个列表。

  • LinkedList相对于ArrayList插入、添加速度更快(因为元素添加到集合位置的时候,不需要像数组那样重新计算或者更新索引)。

  • LinkedList需要更多的内存,因为ArrayList 的每个索引的位置是实际的数据,而LinkedList中的每个节点中存
    储的是实际的数据和前后节点的位置(-个LinkedList实例存储了两个值: Node first 和Node last 分别
    表示链表的其实节点和尾节点,每个Node实例存储了三个值: E item,Node next,Node pre) 。
    什么场景下更适宜使用LinkedList,而不用ArrayList)

6、高并发中的集合有哪些问题? (30k-50K)
第一代线程安全集合类

Vector、Hashtable
是怎么保证线程安排的:使用synchronized修饰方法
缺点:效率低下

第二代线程非安全集合类

ArrayList、HashMap
线程不安全,但是性能好,用来替代Vector、Hashtable
使用ArrayList. HashMap, 需要线程安全怎么办呢?
使用Collections.synchronizedListlist);
Collections.synchronizedMap(m);
底层使用synchronized代码块锁虽然也是锁住了所有的代码,但是锁在方法里边,并所在方法外边性能可以理解
为稍有提高吧。毕竟进方法本身就要分配资源的

*第三代线程安全集合类

在大量并发情况下如何提高集合的效率和安全呢?
java.util.concurrent.*
ConcurrentHashMap:
CopyOnWriteArrayList : .
CopyOnWriteArraySet:注意 不是CopyOnWriteHashSet*
底层大都采用Lock锁(1 .8的ConcurrentHashMap不使用Lock锁), 保证安全的同时,性能也很高。

7、JDK1.8的新特性有哪些?(20K-30K)
8Java接口和抽象类有哪些区别?

相同:
1.不能够实例化
2.可以将抽象类和接口类型作为引用类型
3.一个类如果继承了某个抽象类或者实现了某个接口都需要对其中的抽象方法全部进行实现,否则该类仍然需要被声明为抽象类

不同:
抽象类:
1.抽象类中可以定义构造器
2.可以有抽象方法和具体方法
3.接口中的成员全都是public的
4.抽象类中可以定义成员变量
5.有抽象方法的类必须被声明为抽象类,而抽象类未必要有抽象方法
6.抽象类中可以包含静态方法
7.一个类只能继承一个抽象类
接口:
1.接口中不能定义构造器
2.方法全部都是抽象方法
3.抽象类中的成员可以是private、默认、protected、 public
4.接口中定义的成员变量实际上都是常量
5.接口中不能有静态方法
6.一个类可以实现多个接口
7.接口中的成员变量只能是public static final类型的。
语义与语法不同:
接口: 描述的是共同特征
抽象类: 描述的概念(本质)

8、hashcode和equals的区别?

hashCode介绍
hashCode()的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象
在哈希表中的索引位置。hashCode(} 定义在JDK的0bject.java中, Java中的任何类都包含有hashCode()函数。
散列表存储的是键值对(key-value),它的特点是:能根据’键"快速的检索出对应的"值"。这其中就利用到了散列码! (可以快速找到所需 要的对象)
为什么要有hashCode
以"HashSet如何检查重复”为例子来说明为什么要有hashCode:
对象加入HashSet时,HashSet会先计算对象的hashcode值来判断对象加入的位置,看该位置是否有值,如果没有HashSet会假设对象没有重复出现。但是如果发现有值,这时会调用equals () 方法来检查两个对象是否真的相同。如果两者相同,HashSet就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。这样就大大减少了equals的次数,相应就大大提高了执行速度。

  • 如果两个对象相等,则hashcode-定也是相同的,两个对象相等对两个对象分别调用equals方法都返回true
  • 两个对象有相同的hashcode值, 它们也不一定是相等的
  • 因此,equals方法被覆盖过,则hashCode方法也必须被覆盖
  • hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(), 则该class的两个对象无论如
    何都不会相等(即使这两个对象指向相同的数据)
String、StringBuffer、 StringBuilder区别及使用场景?
  • String是final|修饰的,不可变,每次操作都会产生新的String对象
  • StringBuffer和StringBuilder都是在原对象上操作
  • StringBuffer是线程安全的,StringBuilder线程不安全的
  • StringBuffer方法都是synchronized修饰的
  • 性能: StringBuilder > StringBuffer > string
  • 场景:经常需要改变字符串内容时使用后面两个
    优先使用StringBuilder,多线程使用共享变量时使用StringBuffer
9、重载和重写的区别

重载: 发生在同一 个类中,方法名必须相同,**参数类型不同、个数不同、顺序不同,**方法返回值和访问修饰符可以不同,发生在编译时报错。
重写😗*发生在父子类中,方法名、参数列表必须相同,**返回值范围小于等于父类,抛出的异常范围小于等于父类访问修饰符范围大于等于父类;如果父类方法访问修饰符为private则子类就不能重写该方法。

10、List和Set的区别
  • list有序的 set无序的
  • List: 有序,按对象进入的顺序保存对象,可重复,允许多个Null元素对象,可以使用Iterator取出所有元索,
    在逐一遍历, 还可以使用get(int index)获取指定下表的元素
  • Set: 无序,不可重复,最多允许有一个Null元索对象,取元索时只能用Iterator接口取得所有元索,在逐一遍.
    历各个元索
11、HashMap和HashTable的区别?底层实现是什么?

区别:

  • (1) HashMap方法没有synchronized修饰, 线程非安全, HashTable线程安全;
  • (2) HashMap允许key和value为null,而HashTable不允许

HashMap和HashTable有什么区别?其底层实现
是什么?
2.底层实现:数组+链表实现
jdk8开始链表高度到8、数组长度超过64,链表转变为红黑树,元素以内部类Node节点存在

  • 计算key的hash值,二次hash然后对数组长度取模,对应到数组下标----->如果没有产生hash冲突(下标位置没有元素),则直接创建Node存入数组;----->如果产生hash冲突,先进行equal此较,相同则取代该元素。不同,则判断链表高度插入链表,链表高度达到8,并且数组长度到64则转变为红黑树,长度低于6则将红黑树转回链表
    ●key为null, 存在下标0的位置
    数组扩容
12、ConcurrentHashMap原理,jdk7和jdk8版本的区别?

jdk8;
数据结构: synchrohized+CAS+Node+红黑树, Node的val和next都用volatile修饰, 保证可见性,查找,替换,赋值操作都使用CAS
锁:锁链表的head节点,不影响其他元素的读写,锁粒度更细,效率更高,扩容时,阻塞所有的读写操作、并发扩容.
读操作无锁:
Node的val和next使用volatile修饰,读写线程对该变量互相可见。不会读到脏数据)
数组用volatile修饰, 保证扩容时被读线程感知

13、如何实现一个IOC容器?

1、配置文件配置包扫描路径
2、递归包扫描获取.class文件[
3、反射、确定需要交给I0C管理的类
4、对需要注入的类进行依赖注入

  • 配置文件中指定需要扫描的包路径
  • 定义一些注解,分别表示访问控制层、业务服务层、数据持久层、依赖注入注解、获取配置文件注解
  • 从配置文件中获取需要扫描的包路径,获取到当前路径下的文件信息及文件夹信息,我们将当前路径下所有
    以.class结尾的文件添加到一个Set集合中进行存储
  • 遍历这个set集合,获取在类上有指定注解的类,并将其交给I0C容器,定义一个安全的Map用来存储这些对
  • 遍历这个I0C容器,获取到每-个类的实例,判断里面是有有依赖其他的类的实例,然后进行递归注入
14、什么是字节码?采用字节码的好处是什么?

java中的编译器和解释器:
Java中引入了虚拟机的概念,即在机器和编译程序之间加入了一层抽象的虚拟的机器。这台虚拟的机器在任何平台上都提供给编译程序-个的共同的接口。
编译程序只需要面向虚拟机,生成虚拟机能够理解的代码,然后由解释器来将虚拟机代码转换为特定系统的机器码执行。在Java中, 这种供虚拟机理解的代码叫做字节码(即扩展名为.class的文件),它不面向任何特定的处理
器,只面向虚拟机。.每一种平台的解释器是不同的,但是实现的虚拟机是相同的。Java源程序经过编译器编译后变成字节码,字节码由虚拟机解释执行,虚拟机将每一条要执行的字节码送给解释器, 解释器将其翻译成特定机器上的机器码,然后在特
定的机器上运行。这也就是解释了Java的编译与解释并存的特点。
Java源代码—>编译器–>jvm可执行的ava字节码(即虚拟指令–v_–jvmn中解释---->机器可执行的二进制机器码—>程序运行。
采用字节码的好处: **
Java语
加粗样式**言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以ava程序运行时比较高效,而且,由于字节码并不专对一种特定的机器,因此, Java程序无须重新编译便可在多种不同的计算机上运行。

15、Java类加载器有哪些?

JDK自带有三个类加载器: bootstrap ClassLoader、ExtClassLoader、AppClassLoader
BootStrapClassLoader是ExtClassLoader的父类加载器,默认负责加载%JAVA_ HOME%6lib 下的jar包和class文件。

  • ExtClassLoader是AppClassLoader的父类加载器,负责加载%)AVA_ HOME96/lib/ext文件夹下的jar包和class类。
  • AppClassLoader是自定义类加载器的父类,负责加载classpath 下的类文件。系统类加载器,线程上下文加载器(程序员最常用的)
    继承ClassLoader实现自定义类加载器
16、双亲委派模型

在这里插入图片描述
双亲委派模型的好处:

  • 主要是为了安全性,避免用户自己编写的类动态替换Java的一 些核心类,比如String。
  • 同时也避免了类的重复加载,因为JVM中区分不同类,不仅仅是根据类名,相同的class文件被不同的
    ClassLoader加载就是不同的两个类
17、java中的异常的体系?(粗粒度的划分)
  • Java中的所有异常都来自顶级父类Throwable
  • Throwable下有两个子类Exception和Error。
  • Error:是程序无法处理的错误,一旦出现这个错误,则程序将被迫停止运行
  • Exception:不会导致程序停止,又分为两个部分RunTimeException运行时异常CheckedException检查异常
  • RunTimeException常常发生在程序运行过程中,导致程序当前线程执行失败。CheckedException常常发生在程序编译过程中,会导致程序编译不通过。
18、 GC如何判断对象可以被回收?
  • 引用计数法:每个对象有一个引用计数属性,新增- -个引用时计数加1,引用释放时计数减1,计数为0时可以
    回收,
  • java采用 :可达性分析法:从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的,那么虚拟机就判断是可回收对象。

引用计数法,可能会出现A引用了B, B又引用了A,这时候就算他们都不再使用了,但因为相互引用计数器。 =1永远无法被回收。

Java中:GC Roots的对象有:

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中JN(即一般说的Native方法)引用的对象

1、可达性算法中的不可达对象并不是立即死亡的,对象拥有一次自我拯救的机会。对象被系统宣告死亡至少要经历两次标记过程:第- -次是经过可达性分析发现没有与GC Roots相连接的引用链,第二次是在由虚拟机自动建立的Finalizer队列中判断是否需要执行finalize(方法。
2、当对象变成(GC Roots)不可达时,GC会判断该对象是否覆盖了finalize方法,若未覆盖,则直接将其回收。否则,若对象未执行过finalize方法,将其放入F-Queue队列,由- -低优先级线程执行该队列中对象的finalize方法。执行finalize方法完毕后,GC会再次判断该对象是否可达,若不可达,则进行回收,否则,对象“复活"。
3、每个对象只能触发一次finalize()方法。
4、由于finalize(方法运行代价高昂,不确定性大,无法保证各个对象的调用顺序,不推荐大家使用,建议遗忘它。

19、 sleep()、wait()、join()、yield() 的区别

1、锁池
所有需要竞争同步锁的线程都会放在锁池当中,比如当前对象的锁已经被其中一个线程得到,则其他线程需要在这个锁池进行等待,当前面的线程释放同步 锁后锁池中的线程去竞争同步锁,当某个线程得到后会进入就绪队列进行等待cpu资源分配。
2、等待池
当我们调用wait ()方法后,线程会放到等待池当中,等待池的线程是不会去竞争同步锁。只有调用了notify ()或notifyAl()后等待池的线程才开始竞争锁,notify () 是随机从等待池选出一个线程放到锁池,而notifAl()
是将等待池的所有线程放到锁池当中。

1、sleep 是Thread类的静态本地方法wait 则是Object类的本地方法。
2、sleep方法不会释放lock, 但是wait会释放,而且会加入到等待队列中。

sleep就是把cpu的执行资格和执行权释放出去,不再运行此线程,当定时时间结束再取回cpu资源,参与cpu的调度,获取到cpu资源后就可以继续运行了。而如果sleep时该线程有锁,那么sleep 不会释放这个锁,而是把锁带着进入了冻结状 态,也就是说其他需要这个锁的线程根本不可能获取到这个锁。也就是说无法执行程序。如果在睡眠期间其他线程调用了这个线程的interrupt方法,那么这个线程也会抛出interruptexception异常返回,这点和wait是- - 样的。

3、sleep方法不依赖于同步 器synchronized,但是wait需要 依赖synchronized关键字。
4、sleep不需要被唤醒(休眠之后推出阻塞),但是wait需要(不指定时间需要被别人中断)。
5、sleep -般用于当前线程休眠,或者轮循暂停操作,wait 则多用于多线程之间的通信。
6、sleep会让出CPU执行时间且强制上下文切换,而wait则不-定, wait 后可能还是有机会重新竞争到锁继续执行的。.

yield () 执行后线程直接进入就绪状态马上释放了cpu的执行权,但是依然保留了cpu的执行资格,所以有可能Cpu下次进行线程调度还会让这个线程获取到执行权继续执行.
join () 执行后线程进入阻塞状态,例如在线程B中调用线程A的join (), 那线程B会进入到阻塞队列,直到线程A结束或中断线程.

20、 线程和进程

1.任务管理器可以有多 个进程,每个进程运行的都是可执行程序, 一个可执行程序就是一个软件,可执行程序的本质就是在计算机当中运行的一块代码。

  • 进程:可以看成是在计算机当中运行的一块代码
  • 线程:可以看成是在计算机当中运行的一小块代码
    2、线程与进程的关系
  1. 一个进程中可以有多个线程,至少得有一个线程;
  2. 上面说一 个进程可以狭隘的看成是一大段代码, 那其实线程也是一段代码。
  3. 线程是进程中的最小单位。
    3、CPU如何处理任务?
  4. 在单位时间时间片上只能执行一个线程
  5. CPU 看到内存中有很多的线程,CPU 在单位时间片(时间片:很微小的时间单位)。CPU上高速切换线程执行。在同一个时间片上,只能处理一个线程

4、线程的作用:看下面两种理解方式:
1、可以将代码中(软件)的某些独立的功能包装起来,单独作为任务交给CPU处理!
2、将需做的某 个功能封装成一个线程体, 该线程可以独立的获得CPU分配的资源从而实现多功能同时运行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cookie3_1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值