您当前的位置: 首页 >  java晴天过后 Java

java面试突击(100题)进大厂就这么简单

java晴天过后 发布时间:2022-06-01 21:26:27 ,浏览量:4

1、谈谈对面向对象思想的理解

首先,谈谈“面向过程”vs“面向对象”

我觉得这两者是思考角度的差异,面向过程更多是以“执行者”的角度来思考问题,而面向对象更多是以“组织者”的角度来思考问题,举个例子,比如我要产生一个0-10之间的随机数,如果以“面向过程”的思维,那我更多是关注如何去设计一个算法,然后保证比较均衡产生0-10的随机数,而面向对象的思维会更多关注,我找谁来帮我们做这件事,比如Random类,调用其中提供的方法即可。

所以,面向对象的思维更多的是考虑如何去选择合适的工具,然后组织到一起干一件事。

好比一个导演,要拍一场电影,那么首先要有男猪脚和女猪脚,然后还有其他等等,最后把这些资源组织起来,拍成一场电影。

再说回我们的程序世界,这个组织者的思维无处不在,比如,我们要开发项目,以三层架构的模式来开发,那么这个时候,我们不需要重复造轮子,只需要选择市面上主流的框架即可,比如SpringMVC,Spring,MyBatis,这些都是各层的主流框架。

2、JDK、JRE和JVM有什么区别?

JDK:Java Development Kit,Java开发工具包,提供了Java的开发环境和运行环境。

包含了编译Java源文件的编译器Javac,还有调试和分析的工具。

JRE:Java Runtime Environment,Java运行环境,包含Java虚拟机及一些基础类库

JVM:Java Virtual Machine,Java虚拟机,提供执行字节码文件的能力

所以,如果只是运行Java程序,只需要安装JRE即可。

另外注意,JVM是实现Java跨平台的核心,但JVM本身并不是跨平台的,不同的平台需要安装不同的JVM

3、Java的基本数据类型

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

注意:String是引用类型

4、==和equals的区别

== 比较的是值

比较基本的数据类型,比较的是数值

比较引用类型:比较引用指向的值(地址)

equals

默认比较也是地址,因为这个方法的最初定义在Object上,默认的实现就是比较地址

自定义的类,如果需要比较的是内容,那么就要学String,重写equals方法

代码案例:测试以下的每道题,你是否能够正确得到答案?

String s1 = new String("zs");
String s2 = new String("zs");
System.out.println(s1 == s2);//false
String s3 = "zs";
String s4 = "zs";
System.out.println(s3 == s4);//true
System.out.println(s3 == s1);//false
String s5 = "zszs";
String s6 = s3+s4;
System.out.println(s5 == s6);//false
final String s7 = "zs";
final String s8 = "zs";
String s9 = s7+s8;
System.out.println(s5 == s9);//true
final String s10 = s3+s4;
System.out.println(s5 == s10);//false

5、final的作用

final修饰类,表示类不可变,不可继承。比如,String,不可变性

final修饰方法,表示该方法不可重写。比如模板方法,可以固定我们的算法

final修饰变量,这个变量就是常量

注意:

修饰的是基本数据类型,这个值本身不能修改

修饰的是引用类型,引用的指向不能修改

比如下面的代码是可以的

final Student student = new Student(1,"Andy");
student.setAge(18);//注意,这个是可以的!

6、String s = "java"与String s = new String("java")

String s = "java";

String s = new String("java");

这两者的内存分配方式是不一样的。

第一种方式,JVM会将其分配到常量池,而第二种方式是分配到堆内存

7、String,StringBuffer,StringBuilder区别

String 跟其他两个类的区别是

String是final类型,每次声明的都是不可变的对象,
所以每次操作都会产生新的String对象,然后将指针指向新的String对象。

StringBuffer,StringBuilder都是在原有对象上进行操作

所以,如果需要经常改变字符串内容,则建议采用这两者。

StringBuffer vs StringBuilder

前者是线程安全的,后者是线程不安全的。
线程不安全性能更高,所以在开发中,优先采用StringBuilder.
StringBuilder > StringBuffer > String

8、接口和抽象类的区别

这个问题,要分JDK版本来区分回答:

  • JDK1.8之前: 语法: 抽象类:方法可以有抽象的,也可以有非抽象, 有构造器接口:方法都是抽象,属性都是常量,默认有public static final修饰 设计: 抽象类:同一类事物的抽取,比如针对Dao层操作的封装,如,BaseDao,BaseServiceImpl接口:通常更像是一种标准的制定,定制系统之间对接的标准例子:1,单体项目,分层开发,interface作为各层之间的纽带,在controller中注入IUserService,在Service注入IUserDao2,分布式项目,面向服务的开发,抽取服务service,这个时候,就会产生服务的提供者和服务的消费者两个角色这两个角色之间的纽带,依然是接口
  • JDK1.8之后:
  • 接口里面可以有实现的方法,注意要在方法的声明上加上default或者static

最后区分几个概念:

  • 多继承,多重继承,多实现
  • 多重继承:A->B->C(爷孙三代的关系)多实现:Person implements IRunable,IEatable(符合多项国际化标准)多继承:接口可以多继承,类只支持单继承

9、算法题-求N的阶乘(手写)

这道算法题一般考查的递归的编程技能,那么我们回顾下递归程序的特点:

1,什么是递归?

递归,就是方法内部调用方法自身
递归的注意事项:
找到规律,编写递归公式
找到出口(边界值),让递归有结束边界
注意:如果递归太多层,或者没有正确结束递归,则会出现“栈内存溢出Error”!
问题:为什么会出现栈内存溢出,而不是堆内存溢出?

2,这道题该怎么写?

规律:N !=(n-1)!*n;

出口:n == 1或n == 0 return 1;

public static int getResult(int n){
    if(n= IntegerCache.low && i  index; i--)
        x = x.prev;
    return x;
}

5,一个思考题,假如我们可以确定要存储1000个元素,那么采用ArrayList和LinkedList,

哪个更耗内存,为什么?

6,LinkedList,要实现在A和B之间插入C,该如何实现,编写伪代码即可

17、如何在双向链表A和B之间插入C?

可以使用伪代码的方式来实现,你的答案是什么?

假设我们定位到了A节点,那么A.next就是B节点,这个是前提。

你的答案是?可以思考过后,再看答案

C.pre = A;

C.next = A.next;

A.next.pre = C;

A.next = C;

18、谈谈HashSet的存储原理

HashSet的存储原理或者工作原理,主要是从如何保证唯一性来说起。

这里面主要有3个问题,需要回答?

第一,为什么要采用Hash算法?有什么优势,解决了什么问题?

第二,所谓哈希表是一张什么表?

第三,HashSet如何保证保存对象的唯一性?会经历一个什么样的运算过程?

大家可以先思考,晚些再补充答案!

首先,我们要明确一点,HashSet底层采用的是HashMap来实现存储,其值作为HashMap的key

public boolean add(E e) {
	return map.put(e, PRESENT) == null;
}

具体关于hashmap的细节再说

第一,为什么要采用Hash算法?有什么优势,解决了什么问题?

解决的问题是唯一性

存储数据,底层采用的是数组

当我们往数组放数据的时候,你如何判断是否唯一?

可以采用遍历的方式,逐个比较,但是这种效率低,尤其是数据很多的情况下

所以,为了解决这个效率低的问题,我们采用新的方式

采用hash算法,通过计算存储对象的hashcode,然后再跟数组长度-1做位运算,得到我们要存储在数组的哪个下标下,如果此时计算的位置没有其他元素,直接存储,不用比较。

此处,我们只会用到hashCode

但是随着元素的不断添加,就可能出现“哈希冲突”,不同的对象计算出来的hash值是相同的,这个时候,我们就需要比较,才需要用到equals方法

如果equals相同,则不插入,不相等,则形成链表

第二,所谓哈希表是一张什么表?

本质是一个数组,而且数组的元素是链表

JDK1.7的版本实现

JDK1.8做了优化

随着元素不断添加,链表可能会越来越长,会优化红黑树

19、谈谈LinkedHashMap和HashMap的区别(重点)

此处,我们好好谈谈HashMap

主要关注几个点:

1,初始化大小是16,如果事先知道数据量的大小,建议修改默认初始化大小。 减少扩容次数,提高性能 ,这是我一直会强调的点
2,最大的装载因子默认是0.75,当HashMap中元素个数达到容量的0.75时,就会扩容。 容量是原先的两倍
3,HashMap底层采用链表法来解决冲突。 但是存在一个问题,就是链表也可能会过长,影响性能
于是JDK1.8,对HashMap做了进一步的优化,引入了红黑树。
当链表长度超过8,且数组容量大于64时,链表就会转换为红黑树
当红黑树的节点数量小于6时,会将红黑树转换为链表。
因为在数据量较小的情况下,红黑树要维护自身平衡,比链表性能没有优势。
这3点非常重要!

其次,LinkedHashMap就是链表+散列表的结构,其底层采用了Linked双向链表来保存节点的访问顺序,所以保证了有序性。

20、谈谈ConcurrentHashMap,HashMap,Hashtable的区别

1,首先,来看看其他几个相关的类

Hashtable是线程安全的,但效率低
HashMap是线程不安全的,但效率高

Collections.synchronizedMap(),工具类提供了同步包装器的方法,来返回具有线程安全的集合对象
性能依然有问题

public static  Map synchronizedMap(Map m) {
    return new SynchronizedMap(m);
}
//在这个类的内部方法实现上,也只是单纯加上了锁
public V put(K key, V value) {
    synchronized (mutex) {
        return m.put(key, value);
    }
}

为解决这样的矛盾问题,所以JDK提供了并发包,来平衡这样的问题(java.util.concurrent)

2,ConcurrentHashMap(重点)

  • 兼顾了线程安全和效率的问题

分析:HashTable锁了整段数据(用户操作是不同的数据段,依然需要等待)
解决方案:把数据分段,执行分段锁(分离锁),核心把锁的范围变小,这样出现并发冲突的概率就变小
在保存的时候,计算所存储的数据是属于哪一段,只锁当前这一段

  • 注意:分段锁(分离锁)是JDK1.8之前的一种的方案,JDK1.8之后做了优化。

JDK1.7跟JDK1.8在ConcurrentHashMap的实现上存在以下区别:

1,数据结构

JDK1.7采用链表的方式,而JDK1.8则采用链表+红黑树的方式

2,发生hash碰撞之后

JDK1.7发生碰撞之后,会采用链表的方式来解决

JDK1.8发生碰撞之后,默认采用链表,但当链表的长度超过8,且数组容量超过64时,会转换为红黑树存储

3,保证并发安全

JDK1.7采用分段锁的方式,而JDK1.8采用CAS和synchronized的组合模式

4,查询复杂度

JDK1.7采用链表的方式,时间复杂度为O(n),而JDK1.8在采用红黑树的方式时,时间复杂度为O(log(n))

更多的java课程学习路线,笔记,面试等架构资料,关注我,主页更多java内容

关注
打赏
查看更多评论

java晴天过后

暂无认证

  • 4浏览

    0关注

    185博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文
立即登录/注册

微信扫码登录