招银网络科技Java面经整理2

以下为牛客网精选面试题,网上资料整理解答。

招银网络科技Java面经2

一面:
(1)自我介绍。
(2)string判等。

“==”判断符号左右两个变量(引用类型)是否指向同一内存地址。
equals()方法判断两个对象是否一样(所有成员的值是否相同)
例1:

String a = "abc";
String b = "abc";
System.out.println(a == b);	//true
System.out.println(a.equals(b));	//true

在例1中,“abc”是放在常量池(constant pool)中的,所以,虽然a,b都等于“abc”,但是内存中只有一份副本,所以“==”的结果为true
例2:

String a = new String("abc");
String b = new String("abc");
System.out.println(a == b);	//false
System.out.println(a.equals(b));	//true

在例2中,new方法决定了两个不同的string "abc"被创建放在了内存heap区(堆上),分别被a和b所指向,因此“==”返回了false

(3)java集合的类层次关系,各种集合容器简单介绍一下。
collection和collections的区别:collection是集合类(collection)的顶级接口,collections是提供了一系列静态方法的集合工具类。
collection的类层次结构:包括set、list和queue接口。其下分别有HashSet,LinkedHasSet,TreeSet;ArrayList,Vector,LinkedList;PriorityQueue。
Map的类层次结构:包括Hashtable,LinkedHashMap,HashMap和TreeMap。
在这里插入图片描述

(4)给出2个kv实例,描述hashmap.put()过程。
HashMap<K,V>是最常用的一种map,在其内部包装了一个Node<K,V>的类,并且用Node型的数组table来存储数据,与ArrayList一样的实现了元素的增删以及扩容等功能。K一般是8种基本类型的封装类和String类,可存储null的键和null的值。
put方法将<K,V>放在map中。如果该key已经存放于map中,则用新值直接替换旧值。
put的返回值:如果该key已经存放在map中,则返回其映射的旧值;如果不存在,则返回null,表示没有该key对应的映射。(也有可能原来的映射是key-null)
当new HashMap实例时并没有初始化其成员变量transientt Node<K,V>[] table,也就是说并没有为table分配内存。只有当put元素时才通过resize方法对table进行初始化。
put方法分为两种情况,bucket是以链表形式存储的还是以树形结构存储的。如果是key已存在则修改旧值并返回旧值;如果key不存在就执行插入操作并返回null。如果是插入操作还要modCount++。当如果是链表存储时,如果插入元素之后超过了TREEIFY_THRSHOLD,还要进行树化操作。
注意:put操作,当发生碰撞时,如果是使用链表处理冲突,执行的是尾插法。这个和ConcurrentHashMap不同,ConcurrentHashMap使用的是头插法。因为其HashEntry的next是final的。
put操作的基本流程:
1、通过hash值得到所在bucket的下标,如果是null表示没有发生碰撞,直接put;
2、如果发生了碰撞,则解决发生碰撞的实现方式:链表还是树;
3、如果找到该key的节点则执行更新操作,无需对modCount增一;
4、如果没有找到该key的节点,则执行插入操作,并对modCount增一;
5、在执行插入操作时,如果bucket中bin的数量超过TREEIFY_THRESHOLD,则要树化。
6、在执行插入操作之后,如果size超过了threshold则要扩容。

(5)任何自定义类都能放入hashmap吗?有什么要求?如何实现?
以下是Object对象API关于equal方法和hashCode方法的说明:
1.If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
2.It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.
以上API说明是对之前2点的官方详细说明
关于第一点,相等(相同)的对象必须具有相等的哈希码(或者散列码),为什么?
想象一下,假如两个Java对象A和B,A和B相等(eqauls结果为true),但A和B的哈希码不同,则A和B存入HashMap时的哈希码计算得到的HashMap内部数组位置索引可能不同,那么A和B很有可能允许同时存入HashMap,显然相等/相同的元素是不允许同时存入HashMap,HashMap不允许存放重复元素。
关于第二点,两个对象的hashCode相同,它们并不一定相同。
也就是说,不同对象的hashCode可能相同;假如两个Java对象A和B,A和B不相等(eqauls结果为false),但A和B的哈希码相等,将A和B都存入HashMap时会发生哈希冲突,也就是A和B存放在HashMap内部数组的位置索引相同这时HashMap会在该位置建立一个链接表,将A和B串起来放在该位置,显然,该情况不违反HashMap的使用原则,是允许的。当然,哈希冲突越少越好,尽量采用好的哈希算法以避免哈希冲突。
Hashset是继承Set接口,Set接口又实现Collection接口,这是层次关系。那么Hashset、Hashmap、Hashtable中的存储操作是根据什么原理来存取对象的呢?
下面以HashSet为例进行分析,我们都知道:在hashset中不允许出现重复对象,元素的位置也是不确定的。在hashset中又是怎样判定元素是否重复的呢?在java的集合中,判断两个对象是否相等的规则是:
1.判断两个对象的hashCode是否相等, 如果不相等,认为两个对象也不相等,完毕
如果相等,转入2(这一点只是为了提高存储效率而要求的,其实理论上没有也可以,但如果没有,实际使用时效率会大大降低,所以我们这里将其做为必需的。)
2.判断两个对象用equals运算是否相等,如果不相等,认为两个对象也不相等;如果相等,认为两个对象相等(equals()是判断两个对象是否相等的关键)
为什么是两条准则,难道用第一条不行吗?不行,因为前面已经说了,hashcode()相等时,equals()方法也可能不等,所以必须用第2条准则进行限制,才能保证加入的为非重复元素。

(6)常见的索引结构。
B-树 B-树结构支持插入、控制操作以及通过管理一系列树根状结构的彼此联通的节点中来做选择。B-树结构中有两种节点类型:索引节点和叶子节点。叶子节点是存储数据的,而索引节点是用来告诉用户存储在叶子节点中的数据的顺序,并帮助用户找到数据。B-树不是二叉树,二叉树只是一种简单的节点层次结构的实现。
B+树 B+树是B-树结构的增强版,尽管B+树支持B-树的所有特性,他们之间最显著的不同点在于B+树中底层数据是按照提及的索引列进行排序的。B+树还通过在叶子节点之间附加引用来优化扫描的性能。
散列表
散列表数据结构是一个简单的概念,他将一种算法应用到给定值中以在底层数据存储系统中返回一个唯一的指针或位置。散列表的优点是始终以线性时间复杂度找到需要读取的行的位置,而不想B-树那样需要跨越多层节点来确定位置。
索引不能为空会导致索引失效。

(7)mysql主键和其他索引的区别。
1、主键一定是唯一性索引,唯一性索引不一定是主键(主键就是能够唯一标识表中某一行的属性或者是属性组,一个表只能有一个主键,但可以有多个候选索引。因为主键可以唯一标识一行记录,所以可以确保执行数据更新、删除的时候不会出现错误的。主键还经常和外键构成参照完整性约束,防止出现数据不一致。数据库管理系统对于主键自动生成唯一索引,所以主键也是一个特殊的索引)
2、一个表中可以有多个唯一索引,但是主键只能有一个
3、主键列不能为空值,而唯一性索引列允许空值;
4、主键也可以由多个字段组成组成复合主键,同时主键也是唯一索引;
5、唯一索引表示索引值唯一,可以由一个或者几个字段组成,一个表可以有多个唯一索引。

(8)谈一谈你对微服务的理解。
在介绍微服务时,首先得先理解什么是微服务,顾名思义,微服务得从两个方面去理解,什么是"微"、什么是"服务", 微 狭义来讲就是体积小、著名的"2 pizza 团队"很好的诠释了这一解释(2 pizza 团队最早是亚马逊 CEO Bezos提出来的,意思是说单个服务的设计,所有参与人从设计、开发、测试、运维所有人加起来 只需要2个披萨就够了 )。 而所谓服务,一定要区别于系统,服务一个或者一组相对较小且独立的功能单元,是用户可以感知最小功能集。
https://blog.csdn.net/wuxiaobingandbob/article/details/78642020

(9)Spring的AOP。
AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志、事务、权限等待。structs2的拦截器涉及就是基于AOP的思想,是个比较经典的例子。
一、AOP基本概念:
1、Aspect(切面):通常是一个类,里面可以定义切入点和通知。
2、JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用。
3、Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around
4、Pointcut(切入点):带有通知的连接点,在程序中主要体现为书写切入点表达式;
5、AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态爱代理,也可以是CGLIB代理,前者基于接口,后者基于子类。
二、Spring AOP
Spring中的AOP代理还是离不开Spring的IOC容器,代理的生成,管理及其依赖关旭都是由IOC容器负责,Spring默认使用JDK动态代理,在需要代理类而不是代理接口的时候,Spring会自动切换为使用CGLIB代理,不过现在的项目都是面向接口编程,所以JDK动态代理相对来说用的还是多一些。
三 、基于注解的AOP配置方式
1.启用@AsjectJ支持
在applicationContext.xml中配置下面一句:

扫描二维码关注公众号,回复: 9259452 查看本文章
<aop:aspectj-autoproxy />

2.通知类型介绍
(1)Before:在目标方法被调用之前做增强处理,@Before只需要指定切入点表达式即可
(2)AfterReturning:在目标方法正常完成后做增强,@AfterReturning除了指定切入点表达式后,还可以指定一个返回值形参名returning,代表目标方法的返回值
(3)AfterThrowing:主要用来处理程序中未处理的异常,@AfterThrowing除了指定切入点表达式后,还可以指定一个throwing的返回值形参名,可以通过该形参名
来访问目标方法中所抛出的异常对象
(4)After:在目标方法完成之后做增强,无论目标方法时候成功完成。@After可以指定一个切入点表达式
(5)Around:环绕通知,在目标方法完成前后做增强处理,环绕通知是最重要的通知类型,像事务,日志等都是环绕通知,注意编程中核心是一个ProceedingJoinPoint

(10)拦截器用来做什么?哪些场景需要用拦截器?
拦截器可以说相当于是个过滤器:就是把不想要的或不想显示的内容给过滤掉。拦截器可以抽象出一部分代码用来完善原来的action。同时可以减轻代码冗余,提高重用率。
比如在登入一个页面时,如果要求用户密码、权限等的验证,就可以用自定义的拦截器进行密码验证和权限限制。对符合的登入者才跳转到正确页面。这样如果有新增权限的话,不用在action里修改任何代码,直接在interceptor里修改就行了。
好处:拦截器也可以让你将通用的代码模块化并作为可重用的类。Structs2中的很多特性都是由拦截器来完成的。
作用:可以构成拦截器栈,完成特定功能。比如日志记录、登录判断、权限检查等作用。

(11)什么是java内存泄漏?举几个例子。
Java内存泄漏引起的原因:
  内存泄漏是指无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成内存空间的浪费称为内存泄漏。
  长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄漏,尽管短生命周期对象已经不再需要,但是因为长生命周期持有它的引用而导致不能被回收,这就是Java中内存泄漏的发生场景。
造成内存泄漏的几种情况:
1、静态集合类引起内存泄漏
  像HashMap、Vector等的使用最容易出现内存泄露,这些静态变量的生命周期和应用程序一致,他们所引用的所有的对象Object也不能被释放,因为他们也将一直被Vector等引用着。
2、当集合里面的对象属性被修改后,再调用remove()方法时不起作用。
3、监听器
  在释放对象的时候却没有去删除这些监听器,增加了内存泄漏的机会。
4、各种连接
  比如数据库连接(dataSourse.getConnection()),网络连接(socket)和io连接,除非其显式的调用了其close()方法将其连接关闭,否则是不会自动被GC 回收的。
5、内部类和外部模块的引用
  内部类的引用是比较容易遗忘的一种,而且一旦没释放可能导致一系列的后继类对象没有释放。此外程序员还要小心外部模块不经意的引用,例如程序员A 负责A 模块,调用了B 模块的一个方法如: public void registerMsg(Object b); 这种调用就要非常小心了,传入了一个对象,很可能模块B就保持了对该对象的引用,这时候就需要注意模块B 是否提供相应的操作去除引用。
6、单例模式
  不正确使用单例模式是引起内存泄漏的一个常见问题,单例对象在初始化后将在JVM的整个生命周期中存在(以静态变量的方式),如果单例对象持有外部的引用,那么这个对象将不能被JVM正常回收,导致内存泄漏。

(12)static方法在哪个内存区域。
JVM内存总体一共分为了 4个部分:
stack segment、
heap segment、
code segment、
data segment)

stack segment(栈):
局部变量:如main函数中声明的str变量。如图中,str,t存在于stack区:
栈中保存基本数据类型的变量和自定义的对象的引用(不是对象),对象本身都存放在堆区中,被执行的方法的也是pull到栈中,当方法执行完后再push出栈。
heap segment(堆)
当new 一个对象的时候,此对象放在了heap segment(堆)当中。t存放在stack中,而new Test()这个实实在在的对象是存在了heap中 。
heap中存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令)
code segment(代码区)
类中方法的话,是存在在 code segment(代码区)中了
data segment(数据区)
static 的变量或者字符串常量存在数据区
static变量与非static变量
static的变量与非static变量存放位置不一样,并且变量的访问权限也不一样。
static变量是全局的,是类的所有对象都能访问的,是所有方法都可以访问的,无论是static修饰的方法还是非static方法都可以访问,没有限制。
而非static变量是私有的,是有访问限制的,就是说是每个对象独有的特有的,并且只有非static方法才可以访问。
static方法只能访问static的变量,没有权限访问非static变量。
static方法中声明的变量可以与非static变量并且是类的属性重名
方法中声明的变量可以与非static变量并且是类的属性重名
这是由于类的属性是存在与对象中的,是在heap中,而方法中的变量是存在与code 区中的,在不同的区中可以重名。
非static方法中可以方访问static变量。
这时因为static变量是共享的,任何方法,任何对象都可以访问
static方法可以访问static属性
static方法中声明的变量name存在code区,而类的属性中的name存在对象中,而对象存在于heap区。所以不会报错。

(13)写代码,使2个线程出现死锁情况。

public class DeadlockExample {
	String resource1 = "资源1";
	String resource2 = "资源2";
	Thread t1 = new Thread("线程1") {
		public void run() {
			while (true) {
				synchronized (resource1) {
					synchronized (resource2) {
						System.out.printf("线程1拥有[%s], 需要[%s]\n", resource1, resource2);
					}
				}
			}
		}
	};
	Thread t2 = new Thread("线程2") {
		public void run() {
			while (true) {
				synchronized (resource2) {
					synchronized (resource1) {
						System.out.printf("线程2拥有[%s], 需要[%s]\n", resource2, resource1);
					}
				}
			}
		}
	};
	public static void main(String a[]) {
		DeadlockExample test = new DeadlockExample();
		test.t1.start();
		test.t2.start();
	}
}

(14)写代码,判断链表是否有环。
思路:

  1. 设置两个指针,一个指针每次前进一步,一个指针每次前进两步,如果它们中有一个先到达链表末尾,那么无环,否则它们一定会相遇。
  2. 相遇后,每次前进一步的指针继续前进,同时另设一个指针从链表头部开始每次前进一步,它们必然会相遇于环的入口。
public class Test{
    public static class LinkedList{
        int data;
        LinkedList next;
        public LinkedList(int data){
            this.data = data;
            this.next = null;
        }
    }
    public static LinkedList isCyclicLinkedList(LinkedList head){
        if(head == null)
            return null;
        LinkedList pOne = head;
        LinkedList pTwo = head;
        if(pTwo.next != null)
            pTwo = pTwo.next.next;
        else
            return null;
        pOne = pOne.next;
        while(pTwo != pOne){
            if(pTwo != null&&pTwo.next != null){
                pTwo = pTwo.next.next;
            }else{
                return null;
            }
            pOne = pOne.next;
        }
        while(head != pOne){
            pOne = pOne.next;
            head = head.next;
        }
        return pOne;
    }
}

(15)集群如何管理session。
一. 何为session
用户使用网站的服务,基本上需要浏览器和web服务器进行多次交互,web服务器如何知道哪些请求是来自哪个会话的?
具体方式为:在会话开始时,分配一个唯一的会话标识(sessionId),通过cookie把这个标识告诉浏览器,以后每次请求的时候,浏览器都会带上这个会话标识来告诉web服务器请求是属于哪个会话的。如果遇到禁用cookie的情况,一般的做法就是把这个会话标识放到url的参数中。在这里插入图片描述
二. 问题
因为会话信息保存在单机上,当我们的应用服务器从一台变成两台后,我们就会遇到session的问题了!
如下图所示,当我们第一次访问网站时请求落到了左边的服务器,那么我的session就创建在左边的服务器上了,如果我们不做处理,就不能保证接下来的请求每次都落在同一边的服务器上了,这就是session问题。
在这里插入图片描述
三. 解决办法:

  1. session sticky
    在web服务器变成多台后,如果我们可以保证同一个会话请求都能在同一个web服务器上处理,那么对于这个会话个体来说,和单机的情况是一样的。这就需要负载均衡器能够根据每次请求的会话标识来进行请求转发。
    有何问题:
    ① 如果有一台web服务器宕机或重启,那么这台机器上的会话数据会丢失
    ② 负载均衡器变成了一个有状态的结点,要保存会话到具体web服务器的映射,要消耗一定的内存。
    在这里插入图片描述
  2. session replication
    web服务器之间增加了会话数据的同步,通过同步就保证了不同web服务器之间的session数据一致,一般的应用容器都支持这种方式。
    问题:
    ① 只要session数据有变化,就需要将数据同步到其他机器上,会带来一定的网络带宽开销
    ② 每台web服务器都要保存所有的session数据,如果整个集群session数很多的话,对内存资源消耗较大。
    该方案不适合集群机器较多的场景。在这里插入图片描述
  3. session数据集中存储
    把session数据集中存储起来,然后不同的web服务器从相同的地方来获取session,存储session数据的方式可以为数据库,也可以使用其他分布式存储系统。
    问题:
    ① 获取session存在延时和不稳定性,不过我们的通信基本在内网,问题不大。
    ② 如果存储session的机器或集群发生问题,就会影响到应用。
    当集群规模较大时,session数较多时,该方案可以考虑。在这里插入图片描述
  4. cookie based
    该方案通过cookie来传递session数据,即把session数据存在cookie中
    问题:
    ① cookie有长度限制,这也就会限制session数据的长度
    ② 安全性,cookie的数据保存在客户端,这就存在安全性的问题,我们需要对写入cookie的session数据做加密处理
    ③ 带宽消耗, 客户端每次都要带着session过来,会消耗一定网络资源
    ④ 性能影响,每次http请求和响应都带有session数据,对web服务器来说,在同样的处理情况下,响应的结果输出越少,支持的并发请求就会越多。
    在这里插入图片描述

二面:
自我介绍。
聊项目细节,画架构,如何优化,能否重构等。
数据量巨大时,如何分库分表,场景、条件及实现方式。

hr面:
首先是查原件,毕业证、学位证、四六级、成绩单。
自我介绍。
简历上的经历询问?
为啥考研?
有啥兴趣爱好?能接受加班吗?手上有offer吗?独生子女吗?爸妈哪儿人?(基本是查户口)

原文链接:https://blog.csdn.net/strawqqhat/article/details/88093942
原文链接:https://blog.csdn.net/qq_26653067/article/details/79728244

发布了18 篇原创文章 · 获赞 4 · 访问量 929

猜你喜欢

转载自blog.csdn.net/weixin_43698561/article/details/103964140