Java基础的一些问题
Posted wangdaxianer
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java基础的一些问题相关的知识,希望对你有一定的参考价值。
ArrayList底层原理?
ArrayList底层采用数组实现,访问特别快,它可以根据索引下标快速找到元素。但添加插入删除等写操作效率低,因为涉及到内存数据复制转移。
ArrayList对象初始化时,无参数构造器默认容量为10,当空间不足时会扩容,扩容后的容量是老容量的1.5倍。Java8的ArrayList源代码第259行,可以看到将原始容量数右移一位,即每次扩充老容量的二分之一,即新增0.5倍,换句话说新容量是老容量的1.5倍。
ArrayList与LinkedList区别?
两者都是List接口的实现类,都是线性数据结构。
ArrayList底层采用数组实现,访问特别快,它可以根据索引下标快速找到元素。但添加插入删除等写操作效率低,因为涉及到内存数据复制转移,添加时超过默认容量限制,也会扩容转移数据;LinkedList是采用双向链表结构,每个节点都会有前驱指向上一个节点,后驱指向下一个节点,没有前驱动节点的则是第一个元素,没有后驱节点的是末尾元素,它做添加插入删除操作时,效率特别高,只需将节点指向修改就可以,但要读取时效率低,由于没有索引,必须从第一个元素循环读取。
说说List排序方法
可用使用Collections.sort(List<T> list)和Collections.sort(List<T> list, Comparator<? super T> c)两个排序方法,实际上这种方法排序都需要比较器。
前一个方法是根据内部比较器排序,即List中的元素必须实现Comparable接口,此接口有个compareTo方法用来比较两个对象的某个属性值。
后一个方法是根据Comparator接口实现类的外部比较器进行排序,Comparator接口有个compare方法用于比较两个对象的某个属性值。
比较结果都是返回int类型,只有3个值,分别是“-1”按升序排、“0”不动、“1”按降序排
Java8后List对象本身有sort()方法进行排序,实际上也是使用上述实现原理,不过它使用java8的拉姆达表达式lambda,代码写起来更简洁。
HashMap底层原理
相关问题1: HashMap怎么解决碰撞问题的?
相关问题2: 这些数据结构中是线程安全的吗?
HashMap底层主要是针对key的算法,与HashSet类似,取key的哈希值模于(modulus)存储空间大小,该模数就是HashMap空间的顺序位置,里面存放了Key值和Value的地址。然而万事无完美,如果两个不同的Key,通过哈希函数得出的实际存储地址相同,那就冲突了。为了存储冲突的多个地址,Java使用了链表,但链表遍历效率较低,Java8改进为红黑树算法。当冲突越严重,性能则越低,空间扩容后可以提高性能。HashMap对象初始化时,默认的空间是16个,并且设了一个扩充因子,默认值是0.75,也就是当哈希表中的条目超过了容量和加载因子的乘积的时候(默认16*0.75=12时),就会扩容,并重新进行哈希操作。虽然扩容后性能可以提升,但扩充过程中会重新哈希,因此扩充过程会消耗时间。
HashMap是线程安全的吗?
不是线程安全的。它是为了克服线程安全的HashTable性能和空指针问题,在Java 1.2中重新设计的键值对数据结构实现类,性能明显提高。它的线程安全可使用Java 1.5推荐的java.util.concurrent包ConcurrentHashMap来实现。
如何保证HashMap线程安全
可使用Java 1.5推荐的java.util.concurrent包ConcurrentHashMap来实现,内部不再使用类似HashTable的synchronized同步锁,而是使用ReentrantLock改进读写锁实现线程安全。
HashMap与HashTable的区别
HashTable是在Java1.0推出的,内部大量使用synchronized同步锁保证线程安全,导致效率低下。HashMap是在Java1.2新集合框架重写时推出的,为了提升性能,而放弃了线程安全。
HashTable不允许存入空Key和空Value。HashMap允许存放空key和空值,当多个空key时,只保留最后一个(相当于HashMap不允许有重复key)。
HashTable继承Dictionary(字典)抽象类来枚举key和value。HashMap不再使用这种方式,使用迭代器或key集合来遍历。
HashTable默认的初始大小为11,之后每次扩充为原来的2n+1。HashMap默认的初始化大小为16,之后每次扩充为原来的2倍。
解决冲突方面,Java8中的HashMap使用了红黑树,提升了性能。
HashMap是有序的吗?
不是。
如何保证Map有序(存放顺序)?
LinkedHashMap可以保证放在的顺序。
TreeMap与LinkedHashMap的区别?
相关问题1: TreeMap与LinkedHashMap排序效率哪个更高?
TreeMap是根据元素的内部比较器进行排序的,它可以根据key值的大小排序;
LinkedHashMap是保持存放顺序的。
TreeMap采用红黑树算法,遍历效率高;
LinkedHashMap采用链表实现,添加删除时效率高,遍历时效率低。
说说Java异常框架
Java异常框架顶级接口为Throwable,有两个重要的类是Error和Exception。
Error是系统级错误,无法挽救的错误,例如VirtualMachineError(虚拟机错误)、BootstrapMethodError(启动方法错误)。
Exception是可处理的异常,它又分RuntimeException(运行时异常)和非RuntimeException(非运行时异常)
RuntimeException运行时异常是不需要显式地通过throws或try…cache处理,常见的有:
NullPointerException - 空指针引用异常
ClassCastException - 类型强制转换异常。
IllegalArgumentException - 传递非法参数异常。
ArithmeticException - 算术运算异常
ArrayStoreException - 向数组中存放与声明类型不兼容对象异常
IndexOutOfBoundsException - 下标越界异常
NegativeArraySizeException - 创建一个大小为负数的数组错误异常
NumberFormatException - 数字格式异常
SecurityException - 安全异常
UnsupportedOperationException - 不支持的操作异常
非RuntimeException运行时异常必须显式地throws向上抛或者try…cache处理,常见的有:
IOException – IO异常
SocketException – 网络异常
SQLException – 数据库操作异常
ReflectiveOperationException – 反射异常
IllegalAccessException – 非法访问异常
ClassNotFoundException – 找不到类异常
FileNotFoundException – 找不到文件异常
常见的非运行时异常类型
IOException – IO异常
SocketException – 网络异常
SQLException – 数据库操作异常
ClassNotFoundException – 找不到类异常
FileNotFoundException – 找不到文件异常
常见的运行时异常类型
NullPointerException - 空指针引用异常
ClassCastException - 类型强制转换异常。
IllegalArgumentException - 传递非法参数异常。
IndexOutOfBoundsException - 下标越界异常
NumberFormatException - 数字格式异常
如何自定义异常框架?
项目中的异常一般是运行时异常,因此继承RuntimeException,并且加上项目中全局的错误编码。然后可以在一个公共拦截点,例如拦截器、AOP拦截自己的异常,转化以公共的消息页面提示给用户。
String类中和Object中的equals方法有什么不同?
Object类源代码149行,equals()方法使用了“==”来将自己与参数传来的对象进行比较,它比较的是对象地址。
String类源代码976行,equals()方法比较了真实的字符串值,效率较低。
String类可以继承吗?
不可,Java的String类在定义时,使用final修饰了
String a = "abc";String b = "abc"; 两者相等吗?c = new String("abc") ,a和c相等吗?
前面是相等的,因为a的值”abc”出现时,已经放在常量池中了,b再赋相同的值,则在常量池取拘留值,因此两者内存地址是相同的。
后者就不行了,new的对象在堆了,改变了内存地址
线程
JAVA多线程的应用场景和应用目的举例
多线程使用能提高性能,提高程序的伸缩性。
项目中的应用场景有很多:
1、后台任务,如:
定时向大量(100w以上)的用户发送邮件;
定时检查订单状态
2、异步处理,如:
用户注册时,异步发送短信验证码或验证邮件
上传图片时,异步生成缩略图
记录日志
3、并行计算,如:
Tomcat就是多线程并行计算的,默认为2000条线程,每个请求就开占用一条线程。
还例如网站首页有很多区块组成,要调用很多接口或服务取出数据,可以使用多线程并行读取,然后所有线程读取完成后再进入模版进行数据渲染
什么是线程池?
core:核心线程数
max:最大线程性
//创建一个单线程的线程池
ExecutorService es=Executors.newSingleThreadExecutor();
//创建一个固定大小的线程池
ExecutorService es1=Executors.newFixedThreadPool(2);
//创建一个可缓存的线程池
ExecutorService es2=Executors.newCachedThreadPool();
//创建一个任务执行的线程池
ExecutorService es3=Executors.newScheduledThreadPool(2);
Runnable与Callable的区别?
(1)Callable规定的方法是call(),Runnable规定的方法是run().
(2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值得
(3)call方法可以抛出异常,run方法不可以
(4)运行Callable任务可以拿到一个Future对象,Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结果,如果线程没有执行完,Future.get()方法可能会阻塞当前线程的执行;如果线程出现异常,Future.get()会throws InterruptedException或者ExecutionException;如果线程已经取消,会跑出CancellationException。取消由cancel 方法来执行。isDone确定任务是正常完成还是被取消了。一旦计算完成,就不能再取消计算。如果为了可取消性而使用 Future 但又不提供可用的结果,则可以声明Future<?> 形式类型、并返回 null 作为底层任务的结果。
进程与线程的区别
根本区别:进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位
在开销方面:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。
所处环境:在操作系统中能同时运行多个进程(程序);而在同一个进程(程序)中有多个线程同时执行(通过CPU调度,在每个时间片中只有一个线程执行)
内存分配方面:系统在运行的时候会为每个进程分配不同的内存空间;而对线程而言,除了CPU外,系统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源。
包含关系:没有线程的进程可以看做是单线程的,如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。
Java中Lock、synchronized的区别?
Java从早期开始通过synchronized关键字支持线程同步,实现同步是要很大的系统开销作为代价的,甚至可能造成死锁。Java内部使用监视器(也称为监视器锁或内部锁)来管理同步。 该监视器绑定到一个对象,例如 当使用synchronized方法时,每个方法共享相应对象的相同监视器。所有隐式监视器都实现了可重入特征。 可重入意味着锁绑定到当前线程。 线程可以安全地多次获取相同的锁而不会遇到死锁(例如,同步方法在同一对象上调用另一个同步方法)。
java.util.concurrency包下的API不支持通过synchronized关键字使用隐式锁定,而是支持Lock接口指定的各种显式锁定。 锁支持各种方法以实现更细粒度的锁控制,因此比隐式监视器更具表现力。
Lock又分为:
ReentrantLock(互斥锁):ReentrantLock类是一个互斥锁,具有与通过synchronized关键字访问但具有扩展功能的隐式监视器相同的基本行为。 顾名思义,这个锁实现了可重入的特性,就像隐式监视器一样。如果另一个线程已经获取了锁,则后续调用lock()会暂停当前线程,直到锁被解锁。 在任何给定时间只有一个线程可以保持锁定。
ReadWriteLock(读写锁):接口ReadWriteLock指定另一种类型的锁,它维护一对锁以进行读写访问。 读写锁定背后的想法是,只要没有人写入此变量,通常可以安全地同时读取可变变量。 因此,只要没有线程保持写锁定,读锁就可以由多个线程同时保持。 在读取比写入更频繁的情况下,这可以提高性能和吞吐量。它们不必等待彼此完成,因为只要没有其他线程持有写锁定,就可以安全地同时获取读锁。
StampedLock(读写改进锁):Java 8附带了一种名为StampedLock的新型锁,它也支持读写锁,就像上面的例子一样。 与ReadWriteLock相比,StampedLock的锁定方法返回由long值表示的戳记。 您可以使用这些标记来释放锁定或检查锁定是否仍然有效。 另外,标记锁支持另一种称为乐观锁定的锁定模式。
Java虚拟机
GC是什么? 为什么要有GC?
Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之一,在使用JAVA的时候,一般不需要专门编写内存回收和垃圾清理代 码。这是因为在Java虚拟机中,存在自动内存管理和垃圾清扫机制。
电脑的内存大小的不变的,当我们使用对象的时候,如使用New关键字的时候,就会在内存中生产一个对象,但是我们在使用JAVA开发的时候,当一个对象使用完毕之后我们并没有手动的释放那个对象所占用的内存,就这样在使用程序的过程中,对象越来越多,当内存存放不了这么多对象的时候,电脑就会崩溃了,JAVA为了解决这个问题就推出了这个自动清除无用对象的功能,或者叫机制,这就是GC,有个好听是名字叫垃圾回收,其实就在用来帮你擦屁股的,好让你安心写代码,不用管内存释放,对象清理的事情了。
GC线程是否为守护线程
是。线程分为守护线程和非守护线程(即用户线程)。
只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。
守护线程最典型的应用就是 GC (垃圾回收器)
谈谈Java语言健壮性的理解
我觉得至少有那么3点吧:
1、Java语言是一种强类型语言,即在编译和运行时进行大量的类型检查,防止不匹配的数据类型的发生
2、Java语言设计有自动收集垃圾功能,防止了内存分配的错误
3、Java语言设计了异常处理机制,它分为不可挽救的错误和可处理的异常两种类型。
Java如何计算某个对象占用的内存数
1、预估法:对象属性中根据基本类型算,将结果相加,但要考虑引用指针占用的空间
2、JDK中Instrumentation接口提供了getObjectSize(Object objectToSize)方法可以计算,
3、第三方工具类:https://github.com/DimitrisAndreou/memory-measurer
4、使用jConsole监控虚拟机内存,但无法精确到某对象的大小
什么是Java系列化?
当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。这个过程就是系列化。Java系列化需要发送方把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。
序列化:把Java对象转换为字节序列的过程。
反序列化:把字节序列恢复为Java对象的过程。
transient是干嘛的
Java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想用 serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。 当一个对象被序列化的时候,transient型变量的值不包括在序列化的表示中,然而非transient型的变量是被包括进去的。
以上是关于Java基础的一些问题的主要内容,如果未能解决你的问题,请参考以下文章