Java面试题——第五篇(Java基础)

1. String类能被继承吗

不能被继承,因为String类有final修饰符,而final修饰的类是不能被继承的。

2. String、StringBuffer、StringBuilder的区别

  • String为字符串常量(因为内部数组value[]有final修饰),而StringBuilder和StringBuffer均为字符串变量,即String对象一旦创建之后该对象是不可更改的,而后两者是变量,可以更改。
  • 在线程安全上,StringBuilder是线程不安全的,StringBuffer是线程安全的,因为StringBuffer在append()方法上添加了sychronized修饰。
  • String变量创建后放入方法区的常量池,而StringBuilder和Stringbuffer则是放入堆中。

3. 构造String对象的方式

  1. String str = “123”;
  • 通过引号直接创建字符串对象,先会从常量池中判断是否存在“123”对象,如果不存在,则先会在常量池中创建该对象,并且返回常量池中“123”对象的引用给str。如果存在,则直接返回引用。
  1. String str = new String(“123”);
  • 首先“123”是一个常量字符串,因此先会在常量池创建“123”字符串对象,然后在堆中在创建一个字符串对象。将“123”的字符数组复制到堆中新创建的对象字符数组中,因此该方式不仅会在堆中,还会在常量池中创建“123”字符串对象。
  1. String str = “123”.intern();
  • 该方法与1类似,当常量池中存在“123”字符串常量时,则直接返回常量池中的符号引用;若不存在,则先在常量池创建“123”字符串对象,然后返回新创建对象的引用。常用 String str = str1.intern();避免经常创建对象。

4. 类的实例化顺序

Java程序的初始化一般遵循3个原则

  • 静态变量优于非静态变量初始化。
  • 父类优先于子类进行初始化
  • 按照成员变量的顺序进行初始化。

当new时,他们的执行顺序为:父类静态变量、父类静态代码块、子类静态变量、子类静态代码块、父类非静态变量、父类非静态代码块、父类构造函数、子类非静态变量、子类非静态代码块、子类构造函数。

5. 继承、实现、依赖、聚合、组合、关联的关系

  • 继承:指一个类继承另一个类,并且可以增加自己的新功能。
  • 实现:是一个类实现了多个接口,表示一个类具有某些接口赋予的功能。
  • 依赖:一个类使用到了另一个类,而且这种使用关系是偶然的,临时性的,比如类B作为参数被A使用。那么A就依赖于B。
  • 关联:关联关系一般在java中使用成员变量来实现,如A中定义了一个类的成员变量B。
  • 聚合:他体现的是一种整体和部分、拥有的关系,相互之间可以分离,拥有各自的生命周期。如公司和员工的关系。
  • 组合:是一种强聚合,整体和部分之间不可分离。

6. 反射的原理,反射创建类实例的方式是什么

反射机制:Java反射机制是在 运行状态 中,对于任意一个类,如果知道这个类的名称,就能够知道这个类的所有属性和方法。对于任意一个对象,如果知道一个实例对象,都能够调用他的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
反射创建类的方式有

  • 直接使用字节码文件获取对应实例,Object o = clazz.newInstance();
  • 对带参数的构造函数的类,先获取到其构造对象,再通过该构造方法类获取实例。如下
    在这里插入图片描述

7. 反射中,Class.forName和ClassLoader的区别

  • ClassLoader是类加载器,通过一个类的全限定名来获取此类的二进制字节流,遵循双亲委派模型,将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance时才会执行。
  • class.forName()方法内部实际上也是调用ClassLoader来实现的,但会对类进行初始化,执行类中的静态代码块,以及对静态变量的赋值操作。

8. 为什么cglib可以对接口实现代理

cglib动态代理是继承并重写目标类,所以目标类和方法不能被声明为final,而接口是可以被继承的。

9 . error和exception的区别,CheckedException和RuntimeException的区别

  • Exception和Error都是继承于Throwable 类,在Java中只有Throwable类型的实例才可以被抛出或者捕获,他是异常处理机制的基本组成类型。
  • error表示系统级的错误和程序不必处理的异常,是java运行环境中的内部错误或者硬件问题,比如:内存资源不足,对于这种错误,程序基本无能为力。
  • exception表示需要捕捉或者需要程序进行处理的异常。

Exception又分为运行时异常,受检查异常

  • CheckedException 必须在编写代码时,使用try…catch捕获
  • RuntimeException:编写代码时可以忽略捕获操作,这种异常是代码编写或者使用过程中通过规范可以避免发生的。

10. 在jdk1.5中引入了泛型,泛型的存在是用来解决什么问题的

  • 类型安全:泛型的好处是在编译时检查类型安全,减少运行时由于对象类型不匹配引发的异常,在没有泛型的时候,程序员在处理容器时,常常需要使用Object类型来存储元素,丢失了元素的具体类型信息,后续操作中,必须使用强制类型转换。
  • 代码复用:在泛型之前,如果需要为不同类型的元素编写相同的集合操作逻辑(如添加、删除、遍历等),程序员需要为每种可能的类型编写一个特定的版本。这不仅繁琐,而且维护起来也很困难。泛型允许程序员编写与类型无关的通用代码,然后在编译时通过类型参数来指定具体的类型,从而实现了代码的复用。这样,同一个集合类可以用于存储不同类型的对象,而无需为每种类型都编写一个专门的类。

11. select、poll和epoll之间的区别

目前支持IO多路复用的系统调用有select、poll和epoll。IO多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪,能够通知程序进行相应的读写操作。但select、poll和epoll本质上都是同步IO,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的。

  1. select
  • select函数监视的文件描述符分为3类,分别是writefds、readfds和exceptfds。调用后select函数会阻塞,直到有描述符就绪,或者超时。当select函数返回后,可以遍历fdset,来找到就绪的描述符。
  • 优点:跨平台性能好
  • 缺点:单个线程能够监视的文件描述符数量存在最大限制,在linux上一般为1024;对socket进行扫描时是线性扫描,即采用轮询的方式,效率低;需要维护一个用来存放大量fd的数据结构,会使得用户空间和内核空间在传递该结构时复制开销大。
  1. poll
  • poll 和select没有区别,他的优点是没有最大连接数的限制,原因是他基于链表来存储,但是同样有一个缺点,大量的fd数组被整体复制于用户态和内核地址之间。
  1. epoll
  • epoll是select和poll的增强版本,他更加灵活,没有描述符数量限制。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次,所有的fd都存入红黑树中。
    epoll支持水平触发和边缘触发,最大的特点是边缘触发,他只告诉线程哪些fd刚刚变为就绪态,并且只会通知一次。还有一个特点是,epoll通过“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似回调机制把该fd放入到双向链表,epoll_wait便可以收到通知。
  • epoll的优点主要有三个方面
    a. 没有最大并发连接的限制,能打开的fd上限远大于1024
    b. 效率提升,不是使用轮训的方式,不会随着fd数量的增加效率下降
    c. 内存拷贝,使用mmap减少了复制开销。

12. final修饰的变量是引用不可变,还是引用的对象不能改变

final 修饰的变量是引用不可变,但是引用的对象还是可以发生改变。

  • 如果final修饰的是一个基本数据类型的变量,那么这个变量就不可以改变。
  • 如果final修饰的是一个引用类型的变量,那么该变量存的是一个内存地址,该地址就不能变了。

13. Java中Comparable和Comparator接口的区别

  • Comparable是排序接口,若一个类实现了Comparable接口,意味着这个类支持排序,不需要再去指定比较器,意味着这个类支持排序。
  • Comparator是比较器接口,我们若需要对某个类集合进行排序,而该类本身不支持排序,那么就可以建立一个“该 类的比较器”来进行排序。

14. sleep()方法和yield()方法的区别

  • sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程运行的机会,yield()方法只会给相同优先级或者更高优先级的线程以运行的机会。
  • 线程执行sleep()方法后转入阻塞状态,而执行yield()方法后转入就绪状态。
  • sleep()方法会抛出InterruptedException,而yield()方法没有声明任何异常。
  • sleep()方法比yield()方法具有更好的可移植性。

如果线程A希望立即结束线程B,则可以对线程B对应的Thread实例调用interrupt方法,如果此刻线程B正在wait、sleep、join,则线程B会立刻抛出InterruptedException,在catch中直接return即可安全的结束线程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值