Java基础面试题(持续更新)

1 面向对象的三大特征:封装,继承.多态

封装:彼此相关数据和操作包围起来,抽象成为一个对象,想要访问对象的数据只能通过已定义的接口。封装不仅仅实现了数据的保护,还把彼此相关联的变量和函数包围了起来。

继承:子类继承父类的字段属性方法,Java这支持单继承

多态:

        多态存在的3个条件:1)继承;2)重写;3)父类引用指向子类对象。

        对于同一个行为,不同的子类对象具有不同的表现形式

2 修饰符public,protected,不写(default),private的区别

 3 = 与 =+的区别

@Test
    public void a17(){
    short a =1;
    a = a + 1; // 报错 因为a为short类型, int = short + int  不兼容的类型: 从int转换到short可能会有损失
    a +=1;// 相当于 a = (short) a + 1
    a = (short) (a + 1);
}

4 八大基本类型

八大基本类型速查表

 5 基本类型的字面值

整数的字面值为int :

@Test
    public void a18(){
    int a = 100;
    int b = 9999999999999999999;//报错 右面的数据字面值是double,float是4字节存不下double类型的数据
}

byte,short,char三种比int小的整数可以用范围内的值直接赋值

    byte b1=127;//对,
    byte b2=128;//错,超出byte范围

浮点数的字面值是double类型

double r =3.14;//对
float r =3.14;//错,右面的数据字面值是double,float是4字节存不下double类型的数据

字面值后缀L D F

long x =99999999999L;//字面值是int类型,需转成long类型的数据,加字面值后缀L即可
float b = 3.0F;//3.0字面值是double类型,加后缀F会变成float类型
double d = 3D;//3字面值是int类型,加后缀D,会变成double类型

进制前缀

0b - 标识这是2进制 ,如:0b0101
0 - 标识这是8进制, 8进制是三位,如: 023
0x - 标识这是16进制,如: 0x0001
\u -标识这是char类型,属于16进制

6 基本类型的类型转换

基本类型的类型转换

小到大(隐式转换) 

byte m = 120;
int n = m;//小转大,右面的m是小类型,给左面的n大类型赋值,可以直接使用
float f = 3.2f; double d = f; -->可以执行

大到小(强制类型转换)

int x = 999;
byte y =(byte)x;//大转小,右面x给左面的y小类型赋值,不可以,需要强制类型转换

到低









byte,short,char→ int→ long→float→double

7 运算规则

计算结果的数据类型,与最大数据类型一致

System.out.println(3/2);//1,int/int,得到的结果类型还是int
System.out.println(3/2d);//1.5,int/double。得到的结果是double

byte,short,char三种比int小的整数,运算时会先自动转换成int

byte a = 1;
byte b = 2;
byte c = (byte)(a+b);
//a+b会自动提升成int类型,右面得运算结果就是int大类型
//给左面的byte小类型赋值,不可以,需要强转。

浮点数运算不精确(bigdecimal解决)

8 用最有效率的方法计算2乘以8?

 2<<3

9 &和&&的区别?

& 为逻辑与   当&的左右两边都为true才返回true  当&的左边已经为false,虽然说已经决定了当前的整个表达式为false 但是依旧需要判断右边的 表达式

&&为短路与  当&的左边表达式已经为false不需要判断右边的表达式,直接返回false,

|与|| 类似:

||:当左边的表达式已经为true时,不需要判断右边的表达式,直接返回true

|:即使左边的表达式以及为true,还需要判断右边的表达式

10 String 是 Java 基本数据类型吗?

不是,String为引用类型,默认值为null ,而8大基本数据类型(括号中为默认值),byte(0) short(0) int(0) long(0L)  float(0.0f) double(0.0)  char('/uoooo'(null)) boolean(flase) 并且Double 也为引用类型

基本数据类型:数据直接存储在栈上

引用数据类型区别:数据存储在堆上,栈上只存储引用地址

11 String 类可以被继承吗?

不行。String 类使用 final 修饰,无法被继承。

12、String和StringBuilder、StringBuffer的区别?

String    StringBuffer    StringBuilder
String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,不仅效率低下,而且浪费大量优先的内存空间 StringBuffer是可变类,和线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象。每个StringBuffer对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量 可变类,速度更快
不可变    可变   可变
 线程安全    线程不安全
 多线程操作字符串   单线程操作字符串

13.初始化考察,请指出下面程序的运行结果。

public class InitialTest {
    public static void main(String[] args) {
        A ab = new B();
        ab = new B();
    }
}
class A {
    static { // 父类静态代码块
        System.out.print("A");
    }
    public A() { // 父类构造器
        System.out.print("a");
    }
}
class B extends A {
    static { // 子类静态代码块
        System.out.print("B");
    }
    public B() { // 子类构造器
        System.out.print("b");
    }
}

执行结果:ABabab,两个考察点:

1)静态变量只会初始化(执行)一次。

2)当有父类时,完整的初始化顺序为:父类静态变量(静态代码块)->子类静态变量(静态代码块)->父类非静态变量(非静态代码块)->父类构造器(构造方法) ->子类非静态变量(非静态代码块)->子类构造器 。

14、重载(Overload)和重写(Override)的区别

1 Overload 重载 定义在用类中 方法名相同参数列表不同(顺序不同,类型不同)的方法,方法返回值与访问修饰符可以不同,发生在编译期

2 Override 重写  发生继承中,方法名相同,参数列表相同

                        子类的返回值类型与抛出的异常范围小于等于父类返回值类型

                        访问修饰符大于等于父类,子类不能重写父类的private方法

15、接口(abstract class)与抽象类(interface)的区别

抽象类只能单继承,接口可以多实现。

抽象类可以有构造方法,接口中不能有构造方法。

抽象类中可以有成员变量,接口中没有成员变量,只能有常量(默认就是 public static final)

抽象类中可以包含非抽象的方法(java 8 新特性),抽象类中的抽象方法类型可以是任意修饰符

16、Error 和 Exception 有什么区别?

Error 和 Exception 都是Throwable 的子类

Error 为系统错误,比如内存溢出,不可能指望程序能处理这样的情况。

Exception 表示需要捕捉或者需要程序进行处理的异常,是一种设计或实现问题,也就是说,它表示如果程序运行正常,从不会发生的情况。程序员自己解决

17、阐述 final、finally、finalize 的区别。

没有任何区别

final 修饰类,该类不能在派生子类,不能被当作父类继承,因此,一个类不能同时被声明为abstract 和 final。

      修饰方法:不能被子类重写

        修饰变量:该变量必须在声明时给定初值,而在以后只能读取,不可修改。 如果变量是对象,则指的是引用不可修改,但是对象的属性还是可以修改的。

finally  finally 是对 Java 异常处理机制的最佳补充  配合try、catch使用,存放在finally中的内容,无论是否出现异常都一定会执行,通常用来释放锁 数据库连接等资源

finalize:Object 中的方法,在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。finalize()方法仅作为了解即可,在 Java 9 中该方法已经被标记为废弃,并添加新的 java.lang.ref.Cleaner,提供了更灵活和有效的方法来释放资源。这也侧面说明了,这个方法的设计是失败的,因此更加不能去使用它。

18 JSON数组结构

 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式

1:JSON 构建由两种结构

(1)键值对集合

(2)值的有序列表(An ordered list of values)。在大部分语言中,它被理解为数组(array)

值(value)可以是双引号括起来的字符串(string)、数值(number)、truefalsenull、对象(object)或者数组(array)。这些结构可以嵌套。

2:JSON具有以下这些形式:

(1) 对象格式

JSON对象是无序的:例子:{“id”:“1”,“name”:“飘香白咖”,“age”:“18”}

                        简单写法:key和数字可以不加""号,例子:{id:1,name:“飘香白咖”,age:18}

(2) array 格式

数组是值(value)的有序集合,例如:[1,“飘香白咖”,29]

(3)value格式(复杂格式):值(value)可以是双引号括起来的字符串(string)、数值(number)、true、false、 null、对象(object)或者数组(array)。这些结构可以嵌套。

例子:{id:1,name:“飘香白咖”,hobby:[[“水煮肉片”,“五香脆骨”],[“御姐”,“萝莉”,“筋肉美女”],{“1”:“前端”,“2”:“后端”}]}
[[“水煮肉片”,“五香脆骨”]为value嵌套,{“1”:“前端”,“2”:“后端”}为object嵌套

19 vue 的生命周期

在这里插入图片描述

 20 Mybatis 是半自动的ORM映射框架 需要自己手写SQL语言

21 :线程的状态流转

 1 : new: 对象初始化之后没有启动线程处于此状态

Thread.start与Thread.run的区别

start()方法是启动一个新的线程,新线程会执行相对应的run方法 start() 不能被重复调用

Run() 方法只是普通的方法调用,在调用线程中顺序执行而已

2 : Running 状态:包含了就绪和运行两种状态,线程调用start()方式时,程序进入就绪状态,等待获取cpu时间片,当成功获取时间片之后,进入运行状态,

线程中的sleep()方法与yieId()方法,wait()方法有什么区别

sleep()方法:是来个Thread类,wait();来自Object类

sleep()方法:使当前线程陷入睡眠状态,当当前线程持有同步锁时,线程不会释放同步锁,sleep()方法可以在任何地方都可以使用,sleep方法在时间到了之后自动恢复,继续执行,

wait()方法:使线程停止,并释放同步锁,让其他线程进入synchronized代码块执行,wait()方法只能在同步控制方法,或者同步代码块中使用,当当前线程被wait()时,必须替他线程调用同一对象的notify()或者notifyAll()才能够重新恢复

yieId()方法:线程执行sleep()方法后进入等待超时(timed_waiting)状态,而执行yidID方法后直接进入到就绪状态。 sleep()方法给其他线程运行机会时,不考虑线程的优先级,因此会给低优先级的线程运行的机会;yieId()方法只会给相同优先级或者更高优先级的线程运行的机会

3:Blacked:线程在进入同步方法或者同步代码块时被阻塞,等待同步锁的线程处于此状态

4:waiting:无期限等待另一个线程执行特定的操作处于此状态,需要被唤醒,负责会一直等待下去,例如执行Object.wait()方法时,必须等待另一个线程执行Object.notify()或Object.notifAll();而Thread.join():则需要等待指定的线程终止

线程的 join() 方法是干啥用的?

用于当前线程等待目标的终止在进行运行:如果一个线程A执行了threadB.join()语句,就是当前A线程等待B线程终止后,在从 threadB.join() 返回继续往下执行自己的代码。

5 timed_waiting:在指定的时间内等待另一个线程执行某项操作的线程处于此状态,与waiting不同,当超过自己设置的时间后,会自动唤醒,避免了无期限的等待

6 terminated:执行完毕已经退出的线程处于此状态

22 死锁的四个必要条件

1: 互斥条件:进程对所分配到的资源进行排他性控制,即在一个阶段内一个资源被一个进程所占用,若有其他进程请求此资源,不可访问,只能等待

2:请求和保持条件:进程已经获得了一个资源,但是有对其他资源发出请求,但是其他资源已经被其他进程所占用,当前进程等待其他进程释放资源,保持自己对当前资源的持有

3:不可剥夺条件:进程已经获得的资源在未使用完毕之前,不可以被其他资源强行剥夺,只能自己释放

4 环路等待:A保持a资源,想要吧b,c资源,而b,c资源分别被B,C进程所持有,但是B,C进程想要a资源,等待A进程释放a资源,但是A进程未获得b,c资源,由于不可剥夺性与请求保持条件,保持持有a资源,陷入私有

23 乐观锁与悲观锁

乐观锁 : 把事情都想好的地方想,认为每次处理业务时,在此时期都不会有人同时修改,所以不会上锁,但是提交事务时,会判断拿到的版本号与准备提交的版本号是否一样.或者和CAS算法实现.乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁

悲观锁:吧所以事情都往快的地方想,认为每次处理业务时,在此时期都会有人同时修改,所以给进程上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。java中的同步锁就是悲观锁

24 Lock锁

因为同步锁有缺陷:当一个线程在执行带锁的方法时,其他需要使用这个方法的线程只能等待 ,等待获取锁的线程释放锁。当进行IO获取sleep时,会影响运行速率.。通过Lock可以知道线程有没有成功获取到锁。

Lock是一个类,通过这个类可以实现同步访问;不是java的内置语言

Lock和synchronized有一点非常大的不同,采用synchronized不需要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用;而Lock则必须要用户去手动释放锁,如果没有主动释放锁(unlock()),就有可能导致出现死锁现象。

25 为什么要使用线程池

每次创建线程都new的话,会消耗系统资源,还会降低系统的稳定性,

使用线程池的话:1 重复利用已创建的线程,降低线程创建和销毁造成的消耗。

                          2 提高响应速度,当任务到达时,任务可以不需要等到线程创建就能立即执行。

                          3 增加线程的可管理型。线程是稀缺资源,使用线程池可以进行统一分配,调优和监控。

26 线程池的运作流程

threadFactory(线程工厂):用于创建工作线程的工厂。

当任务到达时,判断是否达到核心线程数(corePoolSize),如果没有达到核心线程数,启动一个核心线程,去执行任务,如果 核心线程数(corePoolSize)<任务<最大线程数(maximumPoolSize) , 并且队列(workQueue)为满,则将保留任务并移交给工作线程的阻塞队列。当   核心线程数(corePoolSize)<任务<最大线程数(maximumPoolSize) 并且任务队列已满,则开启一个非核心线程来运行任务,当任务大于最大线程数(maxxinumPoolSize),则拒绝策略(handler),

保持存活时间(keepAliveTime):当线程池线程大于核心线程数,非核心线程池的空闲时间大于保持存货时间(keepAliveTime)会销毁

 27 java自带的三个进程池

同步队列:SynchronousQueue:同步Queue,属于线程安全的BlockingQueue的一种,此队列设计的理念类似于"单工模式",对于每个put/offer操作,必须等待一个take/poll操作,类似于我们的现实生活中的"火把传递":一个火把传递地他人,需要2个人"触手可及"才行. 因为这种策略,最终导致队列中并没有一个真正的元素
 

 newCachedThreadPool: 没有核心线程,非核心线程最大,队列为同步队列(SynchronousQueue),大小为1,有几个任务就有几个非核心线程,运行效率快

newFixedThreadPool : 没有非核心线程,核心线程数量(corePoolSize)等于最大线程数量(maximumPoolSize).队列为无界队列(LinkedBlockingQueue) 运行效率看核心线程数量

newsigleThreadPool : 没有非核心线程,,最大线程数量等于核心线程数量等于1:队列为无界队列

,线程安全,只有一个线程执行任务,运行效率慢

28 为什么不使用java自带的线程池

因为会产生OOM(内存溢出) : newCachedThreadPool虽然不会产生OOM,但是会内存100%,应为队列本质为数据结构,而数据结构的本质是用来存储的

29 提交优先级,与执行优先级

提交优先级 : 核心线程 --- 队列 --- 非核心线程

执行优先级 : 核心线程 --- 非核心线程 ---队列

30 线程池有哪些拒绝策略?

1 AbortPolicy : 中止策略 默认的中止策略,直接抛出  RejectedExecutionException  异常,调用者可以捕获这个异常,然后根据需求编写自己的处理代码。

2 DiscardPolicy:抛弃策略。什么都不做,直接抛弃被拒绝的任务。

3 DiscardOldestPolicy:抛弃最老策略。抛弃阻塞队列中最老的任务,相当于就是队列中下一个将要被执行的任务,然后重新提交被拒绝的任务。如果阻塞队列是一个优先队列,那么“抛弃最旧的”策略将导致抛弃优先级最高的任务,因此最好不要将该策略和优先级队列放在一起使用。

4 CallerRunsPolicy:调用者运行策略。在调用者线程中执行该任务。该策略实现了一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将任务回退到调用者(调用线程池执行任务的主线程),由于执行任务需要一定时间,因此主线程至少在一段时间内不能提交任务,从而使得线程池有时间来处理完正在执行的任务。

31 集合

集合 : 集合的英文名称是Collection,是用来存放对象的数据结构,而且长度可变,可以存放不同类型的对象,并且还提供了一组操作成批对象的方法.Collection接口层次结构 中的根接口,接口不能直接使用,但是该接口提供了添加元素/删除元素/管理元素的父接口公共方法.
由于List接口与Set接口都继承了Collection接口,因此这些方法对于List集合和Set集合是通用的.

32 为什么要使用集合:

数组的长度是不可变的,插入,删除元素频繁,访问方法单一(数组下标),因为数组的长度不可变,所以数组的扩容(插入)与缩容(删除),所以不是直接操作原数组,而是创建新长度的数组,可用泛型约束

 33 List、Set、Map三者的区别?

List接口 : 有序列表,有下标,可重复,可存null,有Arraylist,与Linklist 2个实现类,

                                  Arraylist 底层为有序动态数组,查询快,增删慢,内部数组默认的初始容量是10,如果不够会以1.5倍的容量增长,

                                    查找:ArrayList数组直接通过index直接定位到数组对应的节点。

                                   增删:ArrayList需要移动目标节点后面的节点(System.arraycopy)

 

                                  LinkList:底层为链表结构,查询慢,增添快,LinkList的内存地址是不联系的,是靠节点之间的地址连接起来的,头节点存储的是数据与下一个节点的地址,尾节点想法,中间阶段存储的是数据,上一个,以及下一个的节点地址

                                   查找:LinkList需要从头节点或者从尾节点开始遍历,知道寻找到目标节点

                                   增删:LinkList只需要修改目标节点前后的next或者prev属性即可

 

Set接口 : 无序列表,无下标,不可重复,有Hashset和TreeSet2个实现类

1 Set是一个不包含重复数据,无序的Collection

2 Set集合中的数据是无序的(因为Set集合没有下标)

3 Set集合中的元素不可以重复 – 常用来给数据去重

4 数据无序且数据不允许重复

5 HashSet : 底层是哈希表,包装了HashMap,相当于向HashSet中存入数据时,会把数据作为K,存入内部的HashMap中。当然K仍然不许重复。

6 TreeSet : 底层是TreeMap,也是红黑树的形式,便于查找数据

Map接口:键值对<k,v>: 有hashMap与treeMap2个实现类

1 Map可以根据键来提取对应的值

2 Map的键不允许重复,如果重复,对应的值会被覆盖

3 Map存放的都是无序的数据

4 Map的初始容量是16,默认的加载因子是0.75

34 HashMap

1 HashMap的结构是数组+链表 或者 数组+红黑树 的形式
2 HashMap底层的Entry[ ]数组,初始容量为16,加载因子是0.75f,扩容按约为2倍扩容
3 当存放数据时,会根据hash(key)%n算法来计算数据的存放位置,n就是数组的长度,其实也就是集合的容量
4 当计算到的位置之前没有存过数据的时候,会直接存放数据
5 当计算的位置,有数据时,会发生hash冲突/hash碰撞
6 解决的办法就是采用链表的结构,在数组中指定位置处以后元素之后插入新的元素
   也就是说数组中的元素都是最早加入的节点
7 如果链表的长度>8时,链表会转为红黑树,当链表的长度<6时,会重新恢复成链表


 

35 hashmap和hashtable支持null值和null键么

hashmap允许多个null值和一个null键,hashtable不允许有任何null值和null键

1 List都可以添加null元素

2  HashMap可以有1个key为null的元素,TreeMap不能有key为null的元素
所以HashSet底层是HashMap,可以有1个null的元素,TreeSet底层是TreeMap,不能有key为null的元素。

36 Map集合的迭代方式

 37 ArrayList 和 Vector 的区别。

ArrayList与Vector几乎一样,唯一的区别是Vector使用的 synchronized 来确保进程安全,,因此在性能上 ArrayList 具有更好的表现。

类似的有 Stringbuilder与 Stringbuffer hashMap与hashtable

38 介绍下 HashMap 的底层数据结构

底层是由“数组+链表+红黑树”组成

 39 为什么改成数组 + 链表 + 红黑树结构

主要是为了在hash严重时(链表过长)的查找性能,使用链表的查询性能为O(n),使用红黑树是O(logn)。

40 那在什么时候用链表?什么时候用红黑树?

对于插入,如果一个同一个索引的链表长度达到8时,而且数组长度大于等于64时,会触发链表节点转化为红黑树,当数组长度小于64时,不会触发链表节点转化为红黑树,会数组扩容,因为此时数据量较小,

对于移除来说,当同一个索引的位置节点在移除后达到6个时,并该索引位置为红黑树节点,会触发红黑树节点转化为链表节点

41 HashMap 的插入流程是怎么样的?

头节点key和入参key是否相等:是判断是否是同一个key,不同的value,因为Map集合,不可以重复,使用后面的entry会覆盖前面的entry。

 42 HashMap 的扩容(resize)流程是怎么样的?

 43 HashMap 和Hashtable 的区别?

hashMap的允许key(只允许1个)与value为null。而hashtable不能为null

hashMap初始容量为15,hashtable初始容量为11,

hashMap的扩容是原来的2倍,hashtable为原来的2倍加1

hashMap是非线程安全的,hashtable是线程安全的

hashMap的hash是重新计算过的,hashtable直接使用hashCode

HashMap 去掉了 Hashtable 中的 contains 方法。

HashMap 继承自 AbstractMap 类,Hashtable 继承自 Dictionary 类。

44 equals方法和hashcode的关系?

1、如果两个对象equals相等,那么这两个对象的HashCode一定也相同

2、如果两个对象的HashCode相同,不代表两个对象就相同,只能说明这两个对象在散列存储结构中,存放于同一个位置

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值