java 面试 - 基础篇

1. JAVA 中的几种基本数据类型是什么,各自占用多少字节。

在JVM中、在内存中、在代码里声明的每一个char、String类型的变量中字符以unicode格式存在。1 字节对应 8 位二进制数,而每位二进制数有 0、1 两种状态,因此 1 字节可以组合出 256 种状态。

  • 第一类:整型 byte short int long
  • 第二类:浮点型 float double
  • 第三类:逻辑型 boolean(它只有两个值可取true false)
  • 第四类:字符型 char

字节:
boolean 布尔型 1/8
byte 字节类型 1
char 字符型 2 一个字符能存储一个中文汉字
short 短整型 2
int 整数类型 4
float 浮点类型(单精度) 4
long 长整形 8
double 双精度类型(双精度) 8
复习:Java为什么选择unicode字符集?
在JVM中、在内存中、在代码里声明的每一个char、String类型的变量中字符以unicode格式存在,以保证java的跨平台性质。
推荐博客:
链接: https://blog.csdn.net/hezh1994/article/details/78899683
链接: https://blog.csdn.net/u013905744/article/details/51923671


2. String 类能被继承吗,为什么。

不可以,因为String类有final修饰符,而final修饰的类是不能被继承的
final类不能被继承,没有子类,final类中的方法默认是final的。
  final方法不能被子类的方法覆盖,但可以被继承。
  final成员变量表示常量,只能被赋值一次,赋值后值不再改变。
final不能用于修饰构造方法。
顺便复习
构造方法可以处于public、protected、private和默认四种访问级别
× 表示不能访问构造函数,即不能实例化类,√表示可以


作用域 当前类 同一包路径下类 子类 其他包路径下类
public
protected ×
default × ×
private × × ×

3. String, Stringbuffer, StringBuilder 的区别。

String 一定比StringBuilder 慢? 看情况
String str = “a”+“b”+“c”; 就比StringBuilder 快,其实等同于str = “abc”;
Stringbuffer线程安全 StringBuilder 线程不安全,建议单线程下用

4. ArrayList 和 LinkedList 有什么区别。

实际考察的是数组与链表的优缺点和区别
1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表(双链表)的数据结构。
2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据
经典题目:
ArrayList list=new ArrayList(); 这种是默认创建大小为10的数组,每次扩容大小为1.5倍
ArrayList list=new ArrayList(20); 这种是指定数组大小的创建,创建时直接分配其大小,没有扩充。 所以,扩充为0次

5. 讲讲类的实例化顺序,比如父类静态数据,构造函数,字段,子类静态数据,构造函数,

字段,当 new 的时候, 他们的执行顺序。

  1. 子类静态成员变量
  2. 子类静态构造函数
  3. 子类实例成员变量
  4. 父类静态成员变量
  5. 父类静态构造函数
  6. 父类实例成员变量
  7. 父类构造函数
  8. 子类构造函数

除了构造函数,子类全部比父类先执行,其顺序是静态先于实例,变量先于函数。然后子父类实例函数都是最后执行。

6. 用过哪些 Map 类,都有什么区别,HashMap 是线程安全的吗,并发下使用的 Map 是什么,他们内部原理分别是什么,比如存储方式, hashcode,扩容, 默认容量等。

HashMap 非线程安全,并发下可采用ConcurrentHashMap 。
HashMap是一个用于存储Key-Value键值对的集合,每一个键值对也叫做Entry
HashMap数组每一个元素的初始值都是Null,需要注意的是,新来的Entry节点插入链表时,使用的是“头插法”。
HashMap初始长度为16,选择16是为了映射到index的hash算法(采用位移算法)。

7. JAVA8 的 ConcurrentHashMap 为什么放弃了分段锁,有什么问题吗,如果你来设计,

你如何设计。
1.8之前ConcurrentHashMap采用 分段锁的机制,实现并发的更新操作,底层由Segment数组和HashEntry数组组成。Segment继承ReentrantLock用来充当锁的角色。
jdk1.8中的实现已经抛弃了Segment分段锁机制,利用CAS+Synchronized来保证并发更新的安全,底层依然采用数组+链表+红黑树的存储结构.

8. 有没有有顺序的 Map 实现类, 如果有, 他们是怎么保证有序的。

LinkedHashMap是有序的(按你插入的顺序);底层为链表
TreeMap 是按key排序的;

9. 抽象类和接口的区别,类可以继承多个类么,接口可以继承多个接口么,类可以实现多个接

口么。
基础知识,不多说了。

10. 继承和聚合的区别在哪。

多聚合,少继承,低耦合,高内聚
封装性、耦合性(一般继承比聚合更加耦合,继承破坏父类封装性。)

11. IO 模型有哪些, 讲讲你理解的 nio, 他和 bio 的区别是啥,谈谈 reactor 模型。

BIO是一个连接一个线程。
NIO是一个请求一个线程。
AIO是一个有效请求一个线程。
Java BIO : 同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。
Java NIO : 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
Java AIO(NIO.2) : 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理,
BIO、NIO、AIO适用场景分析:
BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。
NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。

反应器设计模式(Reactor pattern)是一种为处理并发服务请求,并将请求提交到一个或者多个服务处理程序的事件设计模式。当客户端请求抵达后,服务处理程序使用多路分配策略,由一个非阻塞的线程来接收所有的请求,然后派发这些请求至相关的工作线程进行处理

12. 反射的原理,反射创建类实例的三种方式是什么。

Class c = Class.forName(“java.lang.String”);
Method ms[] = c.getDeclaredMethods();
System.out.println(ms[0].toString());
获得Class c 这一步的三种方式:
(1)通过对象—对象.getClass()来获取c(一个Class对象)
Person p=new Person(“Jack”, 23); Class c=p.getClass();//来自Object方法
(2) 通过类(类型)—任何数据类型包括(基本数据类型)都有一个静态的属性class ,他就是c 一个Class对象
Class c=Person.class; Class c2=int.class;
(3) 通过字符串(类全名 )—能够实现解耦:Class.forName(str)
Class c=Class.forName(“cn.hncu.reflect.test.Person”);

13. 反射中, Class.forName 和 ClassLoader 区别。

java中class.forName()和classLoader都可用来对类进行加载。
class.forName()前者除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块。
而classLoader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块

14. 描述动态代理的几种实现方式,分别说出相应的优缺点。

JDK代理(Proxy.newProxyInstance()方法创建动态代理)和CGLIB代理
CGLib创建的动态代理对象性能比JDK创建的动态代理对象的性能高不少,但是CGLib在创建代理对象时所花费的时间却比JDK多得多,所以对于单例的对象,因为无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更为合适一些。同时,由于CGLib由于是采用动态创建子类的方法,对于final方法,无法进行代理

15. 动态代理与 cglib 实现的区别。

JDK动态代理只能对实现了接口的类生成代理,而不能针对类 。
CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法 。
因为是继承,所以该类或方法最好不要声明成final ,final可以阻止继承和多态。
jdk中的动态代理通过反射类Proxy和InvocationHandler回调接口实现,要求委托类必须实现一个接口,只能对该类接口中定义的方法实现代理,这在实际编程中有一定的局限性

16. 为什么 CGlib 方式可以对接口实现代理。

CGLIB是通过ASM生成java 字节码从而动态的产生代理对象

17. final 的用途。

final类不能被继承,因此final类的成员方法没有机会被覆盖,默认都是final的。在设计类时候,如果这个类不需要有子类,类的实现细节不允许改变,并且确信这个类不会载被扩展,那么就设计为final类。
复习:
CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法 。
因为是继承,所以该类或方法最好不要声明成final

18. 写出三种单例模式实现。

主要注意单例模式中有一种创建方式采用【双检查】模式。其中涉及指令重排问题:
指令重排是什么意思呢?比如java中简单的一句 instance = new Singleton,会被编译器编译成如下JVM指令:
memory =allocate(); //1:分配对象的内存空间
ctorInstance(memory); //2:初始化对象
instance =memory; //3:设置instance指向刚分配的内存地址
但是这些指令顺序并非一成不变,有可能会经过JVM和CPU的优化,指令重排成下面的顺序:
memory =allocate(); //1:分配对象的内存空间
instance =memory; //3:设置instance指向刚分配的内存地址
ctorInstance(memory); //2:初始化对象

19. 如何在父类中为子类自动完成所有的 hashcode 和 equals 实现?这么做有何优劣。

直接重写equals方法和hashcode方法

20. 请结合 OO 设计理念,谈谈访问修饰符 public、 private、 protected、 default 在应用设计中的作用。

Public(一般公共方法)、 private(业务内部自己调用)、 protected(工具类下有子类)、 default(较少使用该修饰符)

21. 深拷贝和浅拷贝区别。

浅拷贝—能复制变量,如果对象内还有对象,则只能复制对象的地址
深拷贝—能复制变量,也能复制当前对象的 内部对象

22. 数组和链表数据结构描述,各自的时间复杂度。

数组要求是一块连续的内存空间来存储,
单链表,每个元素除了存储本身的值外还存储了前驱的引用也就是存储了前驱所在的内存地址信息。这样像链条一样把各元素保证了在逻辑上的连续性。
双链表就是不仅存储了前驱的引用还存储了后继的引用

23. error 和 exception 的区别, CheckedException, RuntimeException 的区别。

Runtime exceptions:
在定义、调用方法时不需要声明会抛出runtime exception;
从java.lang.RuntimeException或java.lang.Error类衍生出来的。
默认情况下Spring事务回滚只有发生RuntimeException才回滚
Checked exceptions:
定义方法时必须声明所有可能会抛出的checked exception;
在调用这个方法时,必须捕获它的checked exception,不然就得把它的exception传递下去;
checked exception是从java.lang.Exception类衍生出来的

24. 请列出 5 个运行时异常。

NullPointerException- 空指针引用异常
ClassCastException - 类型强制转换异常。
ArrayStoreException - 向数组中存放与声明类型不兼容对象异常
IndexOutOfBoundsException - 下标越界异常
NumberFormatException - 数字格式异常

25. 在自己的代码中,如果创建一个 java.lang.String 类,这个类是否可以被类加载器加载?为什么。

不能,双亲委派模型来加载

26. 说一说你对 java.lang.Object 对象中 hashCode 和 equals 方法的理解。在什么场景下需要重新实现这两个方法。

对象排序场景下,或者对象进行比较场景。

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

解决强制转换带来的内存消耗

28. 这样的 a.hashcode() 有什么用,与 a.equals(b)有什么关系。

算出hashcode,比较值相等,则hashCode相等

29. 有没有可能 2 个不相等的对象有相同的 hashcode。

30. Java 中的 HashSet 内部是如何工作的。

HashSet 内部使用 HashMap 。它将元素存储为键和值。
(译者注:HashSet 把存储的值作为 key)

31. 什么是序列化,怎么序列化,为什么序列化,反序列化会遇到什么问题,如何解决。

序列化: 将数据结构或对象转换成二进制串的过程。
反序列化:将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程。
有些特定对象无法反序列化
对象要实现了Serializable 接口
如果序列化和反序列化的serialVersionUID不同则反序列化失败,因为java是通过这个来进行序列化验证的。因此最好还是要定义serialVersionUID
序列化保存的是对象的状态,静态变量属于类的状态,因此 序列化并不保存静态变量
Transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null
一个子类实现了 Serializable 接口,它的父类都没有实现 Serializable 接口,序列化该子类对象,然后反序列化后输出父类定义的某变量的数值,该变量数值与序列化时的数值不同。反序列化时,为了构造父对象,只能调用父类的无参构造函数作为默认的父对象。

32. java8 的新特性。

lambda表达式,函数式编程

猜你喜欢

转载自blog.csdn.net/gzt19881123/article/details/83927373
今日推荐