面试 - 基础

原创
2020/02/24 16:45
阅读数 198

Object有哪些方法?
clone,实现对象的浅复制。
getClass,final方法,获得运行时类型。
toString,该方法用得比较多,一般子类都有覆盖。
finalize,该方法用于释放资源。因为无法确定该方法什么时候被调用,很少使用。
equals,该方法是非常重要的一个方法。一般equals和==是不一样的,但是在Object中两者是一样的。子类一般都要重写这个方法。
hashCode,该方法用于哈希查找,重写了equals方法一般都要重写hashCode方法。这个方法在一些具有哈希功能的Collection中用到。一般必须满足obj1.equals(obj2)==true。可以推出obj1.hash- Code()==obj2.hashCode(),但是hashCode相等不一定就满足equals。不过为了提高效率,应该尽量使上面两个条件接近等价。
wait,wait方法就是使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait()方法一直等待,直到获得锁或者被中断。wait(long timeout)设定一个超时间隔,如果在规定时间内没有获得锁就返回。
notify,该方法唤醒在该对象上等待的某个线程。
notifyAll,该方法唤醒在该对象上等待的所有线程。

类的初始化顺序?
Static Field Initial (静态变量) 
Static Patch Initial (静态初始化块) 
Field Initial (变量)
Field Patch Initial (初始化块)
Structure Initial (构造器)

Map有哪些实现类?
HashMap无序的
LinkedHashMap 有序的
TreeMap 默认升序。

HashMap的工作原理
HashMap使用数组和链表的形式存储数据。
HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,让后找到bucket位置来储存值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞了,对象将会储存在链表的下一个节点中。 HashMap在每个链表节点中储存键值对对象。

HashMap、HashTable的区别?
HashMap是HashTable的轻量级实现(非线程安全的实现),他们都完成了Map接口。主要的区别有:线程安全性,同步(synchronization),以及速度。

放入HashMap中的类需重写哪些方法?
使用HashMap,如果key是自定义的类,就必须重写hashcode()和equals()。

ConcurrentHashMap和HashTable的区别?
HashTable(同一把锁):使用synchronized来保证线程安全,但效率非常低下。当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用put添加元素,另一个线程不能使用put添加元素,也不能使用get,竞争会越来越激烈效率越低。
ConcurrentHashMap(分段锁):(锁分段技术)每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。
首先将数据分为一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问。ConcurrentHashMap是由Segment数组结构和HahEntry数组结构组成。Segment是一种可重入锁ReentrantLock,扮演锁的角色。HashEntry用于存储键值对数据。一个ConcurrentHashMap里包含一个Segment数组。Segment的结构和Hashmap类似,是一种数组和链表结构,一个Segment包含一个HashEntry数组,每个HashEntry是一个链表结构的元素,每个Segment守护着一个HashEntry数组里的元素,当对HashEntry数组的数据进行修改时,必须首先获得对应的Segment。

ConcurrentHashMap的并发度是什么?
ConcurrentHashMap的并发度就是Segment的大小,默认为16。

HashMap、HashTable、ConcurrentHashMap的区别?
HashMap 和 HashTable的父类不同,HashMap是AbstMap,HashTable是Dictionary类。
HashMap的初始容量16,HashTable初始容量11。
HashMap扩容机制必须是2的倍数,HashTable不需要。
线程安全性,HashMap线程不安全,HashTable线程安全。
HashMap允许null ,HashTable不允许null。
遍历方式也不同HashMap使用Iterator,HashTableEnumeration
计算hash值的方法不同 ConcurrentHashMap也是线程安全的HashMap实现的一种;使用cas操作来修改以保证数据的原子性。

TCP、UDP的区别?
TCP是面向连接的,UDP是面向报文的(即发送的时候不需要建立连接)。
TCP是提供可靠服务的,请求有序,UDP尽最大努力交付,不保证服务可靠。
TCP能有拥塞控制,UDP没有拥塞控制。
TCP是点对点,UDP可以1对1,1对多,多对1通信。
TCP首部开销20字节,UDP只有8。

POST、GET的区别?
GET数据传输是,只允许ASCII,参数位于URL上,URL最大长度限制2k,重复提交无害,可为书签,可以被缓存,所以安全性差。
POST允许二进制数据使用多重编码,参数位于请求体重,没有最大长度限制,提交有害,不可为书签也不能被缓存,安全性较高。
POST两次TCP请求,GET一次TCP请求。

如何高效安全地删除一个List中的一个元素?
循环删除List中多个元素的,应该使用迭代器Iterator方式。

字节流和字符流的区别和联系?
字节流在操作的时候本身是不会用到缓冲区(内存)的,是与文件本身直接操作的,而字符流在操作的时候是使用到缓冲区的。字节流在操作文件时,即使不关闭资源(close方法),文件也能输出,但是如果字符流不使用close方法的话,则不会输出任何内容,说明字符流用的是缓冲区,并且可以使用flush方法强制进行刷新缓冲区,这时才能在不close的情况下输出内容。

线程安全的含义? 
就是线程同步的意思,就是当一个程序对一个线程安全的方法或者语句进行访问的时候,其他的不能再对他进行操作了,必须等到这次访问结束以后才能对这个线程安全的方法进行访问。
synchronized修饰的方法或者代码块,所有加上synchronized和块语句,在多线程访问的时候,同一时刻只能有一个线程能够用。
volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最的值。volatile很容易被误用,用来进行原子性操作。

Thread和Runnable如何使用写段小代码
Thread

public class Example {
	public static void main(String[] args){
		MyThread myThread=new MyThread();
		myThread.start(); 
		while (true){ 
			System.out.println("Main方法在运行");
		}
	}
 }

public class MyThread extends Thread{
	public void run(){                                                                                
		while(true){
			System.out.println("MyThread类的run()方法在运行");
		}
	}
 }

Runnable

public class Example {
	public static void main(String[] args){
		MyThread myThread=new MyThread(); 
		Thread thread=new Thread(myThread);
		thread.start();
		while (true){
			System.out.println("Main方法在运行");
		}
	} 
} 

public class MyThread implements Runnable{
	public void run(){                                                                                
		while(true){
			System.out.println("MyThread类的run()方法在运行");
		}
	}
}

Synchronized和Lock类的区别?
Synchronized:
存在层次:是Java的关键字,在jvm层面上。
锁的释放:以获取锁的线程执行完同步代码,释放锁,线程执行发生异常,jvm会让线程释放锁。
锁的获取:假设A线程获得锁,B线程等待。如果A线程阻塞,B线程会一直等待。
锁状态:无法判断。
锁类型:可重入、不可中断、非公平。
性能:少量同步。
Lock:
存在层次:是Java的关键字,在jvm层面上;Lock是一个类。
锁的释放:在finally中必须释放锁,不然容易造成线程死锁。
锁的获取:分情况而定,Lock有多个锁获取的方式,具体下面会说道,大致就是可以尝试获得锁,线程可以不用一直等待。
锁状态:可以判断。
锁类型:可重入、可判断、可公平(两者皆可)。
性能:大量同步。

CountDownLatch和CyclicBarrier的区别?
CountDownLatch:
减计数方式。
计算为0时释放所有等待的线程。
计数为0时,无法重置。
调用countDown()方法计数减一,调用await()方法只进行阻塞,对计数没任何影响。
不可重复利用。
CyclicBarrier:
加计数方式。
计数达到指定值时释放所有等待线程。
计数达到指定值时,计数置为0重新开始。
调用await()方法计数加1,若加1后的值不等于构造方法的值,则线程阻塞。
可重复利用。

Executors的用法?
Executors提供四种线程池:
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO,LIFO,优先级)执行。

ThreadLocal、Volatile、Synchronized的区别?
ThreadLocal是线程的局部变量,是每一个线程所单独持有的,其他线程不能对其进行访问。当使用ThreadLocal维护变量的时候为每一个使用该变量的线程提供一个独立的变量副本,即每个线程内部都会有一个该变量,这样同时多个线程访问该变量并不会彼此相互影响,因此他们使用的都是自己从内存中拷贝过来的变量的副本,这样就不存在线程安全问题,也不会影响程序的执行性能。
Volatile主要是用来在多线程中同步变量。在一般情况下,为了提升性能,每个线程在运行时都会将主内存中的变量保存一份在自己的内存中作为变量副本,但是这样就很容易出现多个线程中保存的副本变量不一致,或与主内存的中的变量值不一致的情况。
Synchronized关键字是Java利用锁的机制自动实现的,一般有同步方法和同步代码块两种使用方式。Java中所有的对象都自动含有单一的锁,当在对象上调用其任意的Synchronized方法时,此对象被加锁,同时在线程从该方法返回之前,该对象内其他所有要调用类中被标记为Synchronized的方法的线程都会被阻塞。

类的加载过程?
首先通过一个类的全限定名来获取此类的二进制字节流;其次将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构;最后在java堆中生成一个代表这个类的Class对象,作为方法区这些数据的访问入口。
总的来说就是查找并加载类的二进制数据。

GC常用算法?
串行收集器(SerialGC)最古老的垃圾回收算法,是以前运行在单核cpu的服务器下的。新生代、老年代都使用串行算法回收。
并行垃圾回收器(ParallelGC)采用多线程的机制是执行GC操作,所以执行GC过程阻塞时间较短。新生代采用的是并行算法,而老年代依然采用的是串行算法。
并发标记扫描垃圾回收器(ConcurrentMarkSweep)是应用程序线程和GC线程交替执行。使用的标记-清除算法,并发阶段会降低吞吐量。它经常被用在那些对于响应时间要求十分苛刻的应用之上。
G1收集器垃圾回收器适用于堆内存很大的情况,他将堆内存分割成不同的区域,并且并发的对其进行垃圾回收。G1也可以在回收内存之后对剩余的堆内存空间进行压缩。并发扫描标记垃圾回收器在STW情况下压缩内存。G1垃圾回收会优先选择第一块垃圾最多的区域。

JVM调优步骤
第1步:分析GC日志及dump文件,判断是否需要优化,确定瓶颈问题点;
第2步:确定JVM调优量化目标;
第3步:确定JVM调优参数(根据历史JVM参数来调整);
第4步:调优一台服务器,对比观察调优前后的差异;
第5步:不断的分析和调整,直到找到合适的JVM参数配置;
第6步:找到最合适的参数,将这些参数应用到所有服务器,并进行后续跟踪。

OOM
内存泄露:申请使用完的内存没有释放,导致虚拟机不能再次使用该内存,此时这段内存就泄露了,因为申请者不用了,而又不能被虚拟机分配给别人用。
内存溢出:申请的内存超出了JVM能提供的内存大小,此时称之为溢出
jstack、jmap可以定位到线程堆栈,根据堆栈信息我们可以定位到具体代码,所以它在JVM性能调优中使用得非常多。

单例有哪些实现方式?最安全简洁是哪种?
饿汉模式(线程安全,调用效率高,但是不能延时加载)
懒汉模式(线程安全,调用效率不高,但是能延时加载)
双重检测锁模式(由于JVM底层模型原因,偶尔会出问题,不建议使用)
静态内部类式(线程安全,调用效率高,可以延时加载)
枚举类(线程安全,调用效率高,不能延时加载,可以天然的防止反射和反序列化调用)

几个常用类的区别?
ArrayList:元素单个,效率高,多用于查询。
Vector:元素单个,线程安全,多用于查询。
LinkedList:元素单个,多用于插入和删除。
HashMap:元素成对,元素可为空。
HashTable:元素成对,线程安全,元素不可为空。

IO与NIO的区别?
NIO即NewIO,这个库是在JDK1.4中才引入的。NIO和IO有相同的作用和目的,但实现方式不同,NIO主要用到的是块,所以NIO的效率要比IO高很多。在JavaAPI中提供了两套NIO,一套是针对标准输入输出NIO,另一套就是网络编程NIO。
IO:面向流,阻塞IO。
NIO:面向缓冲,非阻塞IO。

BIO、NIO、AIO
BIO:
同步阻塞IO操作。
每个连接都需要占用一个线程,没有读到IO流的时候都处于阻塞。
少量的连接使用非常高的带宽,一次发送大量的数据。
NIO:
一种同步非阻塞的I/O。
每个连接都需要注册一个事件监听(select/poll/epoll),任何时候立即返回IO流的当前数据。
如果需要管理同时打开的成千上万个连接,这些连接每次只是发送少量的数据。
AIO:
异步非阻塞I/O。
AIO在操作系统层面, 只支持磁盘IO, 不支持网络IO, 并且上层(nodejs,Java NIO)都会选择用线程池+BIO来模拟文件AIO。

TCP/IP协议三次握手?
第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;SYN:同步序列编号(SynchronizeSequenceNumbers)。
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态。
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
完成三次握手,客户端与服务器开始传送数据。

哪些情况FullGC?
SystemGC。
老年代空间不足。
永久代空间不足。
MinorGC晋升大小大于老年代剩余大小。
堆中分配大对象,老年代装不下的时候。
CMS出发FullGC的两种情况,年轻代晋升老年代没有足够的连续空间,或者在CMS并发收集未完成之前老年代就已经满了。

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部