java面试阿里第1期--一面(1)linkedlist,arraylist(2)String(3)接口和抽象类(4)内部,匿名内部类(5)阻塞io和非阻塞io(6)Spring(7)java内存泄漏

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/JAVA_I_want/article/details/102539522

1. linkedlist、arraylist区别,内存分配上呢?

区别:
ArrayList的底层实现为数组存储在内存中,线程不同步。可通过数组下标的形式进行查找,所以在查询方面的效率较为出色,常用在查询较多的情景下。
LinkedList的底层实现为链表形式,也为线程不同步。而链表的底层也决定了它在查询方面不如数组底层的ArrayList而在指定位置插入等修改操作下,性能优于ArrayList。
内存分配:
LlinkedList:
1.为双向链表,每个节点包含prev、item、next.
2.addFirst(E e) 、addLast(E e) 头尾新增对LinkedList内存开销是统一的, 主要是分配一个内部Entry对象。
3.中间插入和删除的开销也是固定的,不会随着列表增加内存分配而增大.
4.查询需要全链表循环,双向链表从头部查询和从尾部查询结果是一致的。由此可看出,LinkedList不支持高效的随机元素访问
ArrayList:

  1. 数组(线性表),动态分配内存
  2. 未指定长度新增数据时默认为10,每次括容oldCapacity + (oldCapacity >> 1)
  3. 数组的最大长度为Integer.MAX_VALUE - 8
  4. 一旦ArrayList的容量发生了变化,申请扩容和数据的复制,都会带来内存及时间上的开销
  5. 在知道容量大小的情况下,最好使用以下声明方式以减少开销
    public ArrayList(int initialCapacity)
  6. 当然也可以使用trimToSize()对集合长度重新修整以节省内存资源.

2. String是否可变,String a + String b是怎么实现的?

注释:
通常情况下,在java中通过以下步骤实现不可变:

  1. 对于属性不提供设值方法
  2. 所有的属性定义为private final
  3. 类声明为final不允许继承
  4. Return deep cloned objects with copied content for all mutable fields in class
    注意:不用final关键字也可以实现对象不可变,使用final只是显示的声明,提示开发者和编译器为不可变。

String不可变:

  1. 首要原因是安全,不仅仅体现在你的应用中,而且在JDK中,Java的类装载机制通过传递的参数(通常是类名)加载类,这些类名在类路径下,想象一下,假设String是可变的,一些人通过自定义类装载机制分分钟黑掉应用。
  2. 性能 String不可变的设计出于性能考虑,当然背后的原理是String pool,不可变的String更好的提高性能。
  3. 线程安全 当多线程访问时,不可变对象是线程安全的。
    一定要变的话,只能通过反射String类,改变value的值.
    String a + String b是怎么实现的:
    先开辟新空间(生成新地址),再将结果存储进去.

3. 接口和抽象类说一下

  1. 接口中所有的方法隐含的都是抽象的。而抽象类则可以同时包含抽象和非抽象的方法.
  2. 类可以实现很多个接口,但是只能继承一个抽象类.
  3. 类如果要实现一个接口,它必须要实现接口声明的所有方法。但是,类可以不实现抽象类声明的所有方法,当然,在这种情况下,类也必须得声明成是抽象的。
  4. 抽象类可以在不提供接口方法实现的情况下实现接口。
  5. Java接口中声明的变量默认都是final的。抽象类可以包含非final的变量。
  6. Java接口中的成员函数默认是public的。抽象类的成员函数可以是private,protected或者是public。
  7. 接口是绝对抽象的,不可以被实例化。抽象类也不可以被实例化,可以被声明,但是,如果它包含main方法的话是可以被调用的。

4.内部类了解吗?匿名内部类了解吗?

内部类:
内部类有两种,一种是静态内部类,一种是非静态内部类。
原理区别:
静态内部类是属于外部类的类成员,是一种静态的成员,是属于类的.
非静态内部类,是属于外部类的实例对象的一个实例成员,也就是说,每个非静态内部类,不是属于外部类的,是属于外部类的每一个实例的. 例如:new School.Teacher()
创建方式区别:
创建静态内部类的实例的时候,只要直接使用“外部类.内部类()”的方式.
创建非静态内部类的实例的时候,必须要先创建一个外部类的实例,然后通过外部类的实例,再来创建内部类的实例,例如:new School().Teader()
匿名内部类:
通常来说,就是在一个内部类,只要创建一次,使用一次,以后就不再使用的情况下,就可以.

5. 阻塞io和非阻塞io说一下,非阻塞io优点是什么?怎么去监听,怎么实现非阻塞的

阻塞IO和非阻塞IO的区别:
在阻塞模式下,若从网络流中读取不到指定大小的数据量,阻塞IO就在那里阻塞着。比如,已知后面会有10个字节的数据发过来,但是我现在只收到8个字节,那么当前线程就在那等下一个字节的到来,就在那等着,啥事也不做,直到把这10个字节读取完,这才将阻塞放开通行。
在非阻塞模式下,若从网络流中读取不到指定大小的数据量,非阻塞IO就立即通行。比如,已知后面会有10个字节的数据发过来,但是我现在只收到8个字节,那么当前线程就读取这8个字节的数据,读完后就立即返回,等另外两个字节再来的时候再去读取。

非阻塞IO优点:
非阻塞IO能够一定程度上减少服务器瞬间的并发线程数,从而提高CPU执行效率。

实现非阻塞IO:
1、状态轮询:轮询操作主要实现的是用户层 select 和 poll 的支持,用户层的 select()/poll() 会调用设备驱动中的 poll() 函数被执行 ,对 poll 函数的扩展是 epoll().select() 和 poll() 系统调用的本质一样,前者是 BSD UNIX 中引入,后者在 System V 中引入。
2、状态订阅;
3、完成回掉;

解析链接:https://blog.csdn.net/qqliyunpeng/article/details/52448119

6.Spring优点是什么,说一下IOC、AOP

Spring 的优点:

  1. 降低了组件之间的耦合性 ,实现了软件各层之间的解耦.
  2. 可以使用容易提供的众多服务,如事务管理,消息服务等
  3. 容器提供单例模式支持
  4. 容器提供了AOP技术,利用它很容易实现如权限拦截,运行期监控等功能
  5. Spring对于主流的应用框架提供了集成支持,如hibernate,JPA,Struts等
  6. Spring属于低侵入式设计,代码的污染极低
  7. 独立于各种应用服务器
  8. Spring的DI机制降低了业务对象替换的复杂性
  9. Spring的高度开放性,并不强制应用完全依赖于Spring,开发者可以自由选择spring的部分或全部

IOC(控制反转):
控制反转(Inversion ofControl)和依赖注入(Dependecy Injection)是同一个概念. 具体的讲:当某个角色需要另外一个角色协助的时候,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在Spring中创建被调用者的工作不再由调用者来完成,因此称为控制反转。创建被调用者的工作由Spring来完成,然后注入调用者, 因此也称为依赖注入.
Spring以动态灵活的方式来管理对象,注入分为两种方式:设置注入和构造注入.
设置注入的优点:直观,自然
构造注入的优点:可以在构造器中决定依赖关系的顺序。

AOP(面向切面编程):
面向切面编程(AOP)完善Spring的依赖注入(DI),面向切面编程在Spring中主要表现为两个方面:
1.面向切面编程提供声明式事务管理.
2.Spring支持用户自定义的切面.
AOP从程序运行角度考虑程序的结构,是对应用执行过程中的步骤进行抽象,从而获得步骤之间的逻辑划分.
AOP框架具有的两个特征:
1.各个步骤之间的良好隔离性.
2.源代码无关性.
Spring的事务管理机制实现的原理,就是通过这样一个动态代理对所有需要事务管理的Bean进行加载,并根据配置在invoke方法中对当前调用的方法名进行判定,并在method.invoke方法前后为其加上合适的事务管理代码,这样就实现了Spring式的事务管理. Spring中的AOP实现更为复杂和灵活,不过基本原理是一致的.

其他解释链接: https://www.cnblogs.com/zhoudi/p/5820513.html

7.Spring bean的生命周期说一下

Spring Bean的生命周期:
(1)实例化Bean:
对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean。
(2)设置对象属性(依赖注入):
实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息以及通过BeanWrapper提供的设置属性的接口完成依赖注入。
(3)处理Aware接口:
接着,Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给Bean:
①如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,此处传递的就是Spring配置文件中Bean的id值;
②如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传递的是Spring工厂自身。
③如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文.
(4)BeanPostProcessor:
如果想对Bean进行一些自定义的处理,那么可以让Bean实现BeanPostProcessor接口,那将会调用postProcessBeforeInitialization(Object obj, String s)方法。由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术.
(5)InitializingBean与init-method:
如果Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法。
(6)如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法.

以上几个步骤完成后,Bean就已经被正确创建了,之后就可以使用这个Bean了。

(7)DisposableBean:
当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的destroy()方法;
(8)destroy-method:
最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。

概括总结:
1.Spring对bean进行实例化,默认bean是单例;
2.Spring对bean进行依赖注入;
3.1.如果bean实现了BeanNameAware接口,Spring将bean的id传给setBeanName()方法;
3.2.如果bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory方法,将BeanFactory实例传进来;
3.3.如果bean实现了ApplicationContextAware接口,它的setApplicationContext()方法将被调用,将应用上下文的引用传入到bean中;
4.如果bean实现了BeanPostProcessor接口,它的postProcessBeforeInitialization方法将被调用;
5.1.如果bean实现了InitializingBean接口,Spring将调用它的afterPropertiesSet接口方法,类似的如果bean使用了init-method属性声明了初始化方法,该方法也会被调用;
6.如果bean实现了BeanPostProcessor接口,它的postProcessAfterInitialization接口方法将被调用;
7.此时bean已经准备就绪,可以被应用程序使用了,他们将一直驻留在应用上下文中,直到该应用上下文被销毁;
8.若bean实现了DisposableBean接口,Spring将调用它的distroy()接口方法。同样的,如果bean使用了destroy-method属性声明了销毁方法,则该方法被调用.

8.Spring bean的类型有哪些

Spring的Bean类型有两种:普通Bean,工厂Bean(FactoryBean). 这两种Bean都被容器管理,但工厂Bean跟普通Bean不同,其返回的对象不是指定类的一个实例,其返回的是该FactoryBean的getObject方法所返回的对象. 在Spring框架内部,有很多地方有FactoryBean的实现类,它们在很多应用如(Spring的AOP、ORM、事务管理)及与其它第三框架(ehCache)集成时都有体现.
补充:
Spring实例化Bean的三种方式:
1. 使用类构造器实例化 [默认的类构造器]

<bean id=“orderService" class="cn.itcast.OrderServiceBean"/>

2.使用静态工厂方法实例化

<bean id="personService" class="cn.itcast.service.OrderFactory" factory-method="createOrder"/>

public class OrderFactory {
    // 注意这里的这个方法是 static 的!
    public static OrderServiceBean createOrder(){   
        return new OrderServiceBean();
    }
}

3.使用实例工厂方法实例化

<bean id="personServiceFactory" class="cn.itcast.service.OrderFactory"/>
<bean id="personService" factory-bean="personServiceFactory" factory-method="createOrder"/>

public class OrderFactory {
    public OrderServiceBean createOrder(){
        return new OrderServiceBean();
    }
}

实例工厂具体案例请参考: https://www.cnblogs.com/aspirant/p/9209268.html

9. java有内存泄漏吗?说一下内存泄漏的原因并举一个例子.

定义: 内存泄漏是指不再被使用的对象或者变量一直被占据在内存中
理论上来说,Java是有GC垃圾回收机制的,也就是说,不再被使用的对象,会被GC自动回收掉,自动从内存中清除。

内存泄漏:

  1. 长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露。尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收,这就是Java中内存泄露的发生场景.
    例如:缓存系统,我们加载了一个对象放在缓存中(例如放在一个全局map对象中),然后一直不再使用它,这个对象一直被缓存引用,但却不再被使用。
    如何检查内存泄漏: 一定要让程序将各种分支情况都完整执行到程序结束,然后看某个对象是否被使用过,如果没有,则才能判定这个对象属于内存泄露.
    补充:如果一个外部类的实例对象的方法返回了一个内部类的实例对象,这个内部类对象被长期引用了,即使那个外部类实例对象不再被使用,但由于内部类持久外部类的实例对象,这个外部类对象将不会被垃圾回收,这也会造成内存泄露。

2.当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则,对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet集合中单独删除当前对象,造成内存泄露。

猜你喜欢

转载自blog.csdn.net/JAVA_I_want/article/details/102539522