面试知识点总结——Java基础

面试知识点总结——Java基础

这里总结网上各大面经上出现的题目,持续整理。

1.     九种基本数据类型的大小,以及他们的封装类。

byte1/float4/short2/double8/int4/char2/long8/boolean

包装类一般首字母大写,除了Integer(int)和Character(char)。

2.     Switch能否用string做参数?

Java7之前无法使用字符串,在Java7以及以后的版本中可以使用字符串。Switch实现对String的操作,主要是对他做了一次hashCode(),而且hashCode()正好返回的是int。

3.     equals==的区别。

(1)比较java基本类型:比较基本类型只能用"==",不能用"equals",这里的"=="比较的是两个基本类型的值

(2)比较包装类和String:"=="比较的是内存地址,"equals"比较的是值

"=="除了比较基本数据之外都是比较的内存地址

"equals"除了没有没有重写equals方法的类之外都是比较的内容

4.     Object有哪些公用方法?

equals()、getClass()、toString()、wait()、notify()、notifyAll()、hashCode()

5.     Java的四种引用,强弱软虚,用到的场景。

强引用:最常见的,不会被GC回收的对象,如 Object obj = new Object();

软引用:可有可无的对象,如果内存空间足够,GC就不会去回收这个对象,如果内存不足,就会回收,软引用可有和ReferenceQueue(引用队列)联合使用,如果软引用所引用的对象呗GC回收,JVM就会把这个软引用加入到引用队列中。

弱引用:也是描述可有可无的对象,和软引用不同的是,它的生命周期更短,在GC的过程中,一旦发现有弱引用的对象,不管当前内存空间是否足够,都会回收它的内存。真是因为这个特性,所以弱引用常用于Map数据结构中,引用占用空间内存较大的对象。

虚引用:也成幽灵引用,他的构造方法必须传递RefenceQueue参数,当GC准备回收一个对象时,发现它还有虚引用,就会在回收前,把虚引用加入到引用队列中,程序可以通过判断队列中是否加入虚引用来判断被引用的对象是否将要GC回收,从而可以在finalize方法中采取措施。

6.     Hashcode的作用。

1)、hashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode是用来在散列存储结构中确定对象的存储地址的;

2)、如果两个对象相同,就是适用于equals(java.lang.Object) 方法,那么这两个对象的hashCode一定要相同;

3)、如果对象的equals方法被重写,那么对象的hashCode也尽量重写,并且产生hashCode使用的对象,一定要和equals方法中使用的一致,否则就会违反上面提到的第2点;

4)、两个对象的hashCode相同,并不一定表示两个对象就相同,也就是不一定适用于equals(java.lang.Object) 方法,只能够说明这两个对象在散列存储结构中,如Hashtable,他们“存放在同一个篮子里”。

再归纳一下就是hashCode是用于查找使用的,而equals是用于比较两个对象的是否相等的。

7.     ArrayListLinkedListVector的区别。

ArrayList和Vector都是基于数组的,ArrayList线程不安全(读写较快),vector线程安全。

LinkedList采用双向链表实现,查询稍慢,插入较快。

8.     StringStringBufferStringBuilder的区别。

String 字符串常量

StringBuffer 字符串变量(线程安全)

StringBuilder 字符串变量(非线程安全)

9.     MapSetListQueueStack的特点与用法。

Map:以键值对形式存入数据,而且键可以是null值,但是只能为一个null值,后面的null值会覆盖前面的。

Set:继承Collection接口,放入数据没有顺序,像HashSet其实就是采用HashMap的key值来来存放Set的value;不能放入重复值,可以存入null值

List:继承Collection接口,可以放入重复值,可以存入null值,放入的是有顺序的

Stack:栈,先进后出,LinkedList也是可以实现栈,但是在java中并没有让LinkedList继承Stack类,而是有一默认的Stack类。Java.util包中的栈继承了Vector,因此Stack类是用数组实现的,而且是线程安全的。

Queue:队列,先进后出,LinkedList及继承了接口,因此可以用LinkedList创建queue;

10.  HashMapHashTable的区别。

HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口,主要区别在于HashMap允许空(null)键值(key),由于非线程安全,效率上可能高于Hashtable。 HashMap允许将null作为一个entry的key或者value,而Hashtable不允许。 HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因为contains方法容易让人引起误解。 Hashtable继承自Dictionary类,而HashMap是Java1.2引进的Map interface的一个实现。 最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多个线程访问Hashtable时,不需要自己为它的方法实现同步,而HashMap 就必须为之提供外同步。Hashtable直接使用对象hashCode,而HashMap重新计算hash值。Hashtable中hash数组默认大小11,增加方式old*2+1,而HashMap默认16,且一定是2的指数。

11.  HashMapConcurrentHashMap的区别,HashMap的底层源码。

HashMap标准链地址法实现

Collections.synchronizedMap(Map map);函数返回的线程安全的HashMap

ConcurrentHashMap是一个经常被使用的数据结构,相比于Hashtable以及Collections.synchronizedMap()(给对象添加对象锁),ConcurrentHashMap在线程安全的基础上提供了更好的写并发能力(多段锁),但同时降低了对读一致性的要求。

在JDK1.5中,伟大的Doug Lea给我们带来了concurrent包,从此Map也有安全的了。ConcurrentHashMap:在hashMap的基础上,ConcurrentHashMap将数据分为多个segment,默认16个(concurrency level),然后每次操作对一个segment加锁,避免多线程锁的几率,提高并发效率。JDK1.8后,ConcurrentHashMap进行了巨大改动。摒弃了Segment(锁段)的概念,而是启用了一种全新的方式实现,利用CAS(Compare and Swap)算法。它沿用了与它同时期的HashMap版本的思想,底层依然由“数组”+链表+红黑树的方式思想(JDK7与JDK8中HashMap的实现),但是为了做到并发,又增加了很多辅助的类,例如TreeBin,Traverser等对象内部类。详见http://blog.csdn.net/qq407388356/article/details/79546165。

12.  TreeMapHashMapLindedHashMap的区别。

HashMap就是标准链地址法实现

TreeMap基于红黑树(Red-Black tree)实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。LinkedHashMap是HashMap的一个子类,它保留插入的顺序。

13.  Collection包结构,与Collections的区别。

Collection叫类集,包含了4个结构,List接口,Set接口,Queue接口,BeanContext接口。对于List接口的标准实现有abstractList类,Vector类,LinkedList类,ArrayList类(同时继承abstractList),copyOnwriteArrayLIst类,对于Set的被一个AbstractSet实现,其他标准继承abstractSet,实现由hashSet类,copyonwriteArraySet类,TreeSet类(跟hashSet类似,也是用TreeMap实现的),ConcurrentSkipArraySet;

使用类集的总用统一管理单列集合容器,对各个容器实现了标准化。易于我们扩展自己的类。

collections是集合类的一个帮助类(工具类),提供了一系列静态方法对集合进行排序,搜索线程安全等操作(Collections.sort(),Collections.Copy(),Colections。sysnchronizedList()Collections.sysnchronizedMap()等)

14.  trycatch finallytry里有returnfinally还执行么?

在执行try块或者catch块里面的return之前,执行finally,如果finally里有return,则直接return。

15.  ExcptionError包结构。OOM你遇到过哪些情况,SOF你遇到过哪些情况。

Thorwable类所有异常和错误的超类,有两个子类Error和Exception,分别表示错误和异常。

运行时异常(非检查性异常):不处理可以通过编译

非运行时异常(检测性异常):不处理不能通过编译

Error一般是程序无法处理的错误,如OutofMemoryError,StackOverFlowError,ThreadDeath错误等。这类一般java虚拟机直接选择线程终止。

OOM(Out of Memory):除了程序计数器外,虚拟机内存的其他几个运行时区域都有发生OOM异常的可能

SOF(Stack Over Flow):对于栈溢出情况,可以通过设置-Xss参数来控制每一线程栈的大小,当减小每个线程栈大小,同时在函数方法内存放比较大或者多的局部变量,就会发生栈溢出。当然sof只放生在虚拟机栈和本地方法栈,不会发生在java堆和方法区中。

16.  Java面向对象的三个特征与含义。

封装、继承、多态。

17.  OverrideOverload的含义去区别。

覆盖和重载。

18.  Interfaceabstract类的区别。

都不能实例化

1)抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。

2)抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。

3)接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。

4)一个类只能继承一个抽象类,而一个类却可以实现多个接口。

java 1.8开始支持接口中定义静态方法、可以有default方法。

19.  Staticclass non static class的区别。

在java中静态类只有静态内部类这一种定义,其他的都是非静态类。

java中静态内部类和非静态内部类的区别:静态内部类相当于外部类的一个静态属性,因此他不能使用外部类的非静态属性和非静态方法。而非静态内部类相当于外部类非静态属性,因此可以使用静态字段和非静态字段或者方法。

静态内部类存在的价值在于:静态内部类可以创建静态属性,而非静态内部类不可以创建静态属性。(内部类只有在需要的时候才会加载),在这里在每个类中用静态main()方法做测试就派上用场了啊。

20.  java多态的实现原理。

重载多态,编译阶段,静态分派,方法参数来确定重载的方法。

重写多态,父类引用指向子类,(动态分派)动态绑定。

21.  实现多线程的两种方法:ThreadRunable

继承Thread类或Runable接口,都重写run()方法。

还可以通过Callable接口和Future接口实现多线程。详见http://blog.csdn.net/qq407388356/article/details/78535703。

22.  线程同步的方法:sychronizedlockreentrantLock等。

synchronized关键字是jvm虚拟机的关键字,在java.util.concurrent.locks命名空间中还有一个Lock接口,和Lock接口的实现类ReentrantLock(可重入锁)。 ReentrantLock可以实现和synchronized关键字相同的功能,而且更为灵活,在极端的情况下性能会更好一些。

23.  锁的等级:方法锁、对象锁、类锁。

synchronized

对象锁,在修饰代码块的时候需要一个reference对象作为锁的对象.

方法锁,在修饰方法的时候默认是当前对象作为锁的对象.

类锁,synchronized修饰静态的方法或代码块

同时介绍一下下面几种锁:

1:可重入锁:只得是如果一个线程获取了一个对象锁以后,如果该线程获取该对象锁锁定的方法块,则会直接进入,而不用重新申请锁;Lock和syschronized都是可重入锁。(可重入就设计到进入一次,计数器+1,没出来一次,计数器-1)

2:可中断锁:如果某一线程A正在执行锁中的代码,另一线程B正在等待获取该锁,可能由于等待时间过长,线程B不想等待了,想先处理其他事情,我们可以让它中断自己或者在别的线程中中断它,这种就是可中断锁。Lock就是可中断锁,而sysnchronized是不可中断的。

3:公平锁:公平锁即尽量以请求锁的顺序来获取锁。比如同是有多个线程在等待一个锁,当这个锁被释放时,等待时间最久的线程(最先请求的线程)会获得该所,这种就是公平锁。非公平锁则可能是随机的或按照其他优先级,有的线程可能永远也不能获得该锁。synchronized就是非公平锁,ReentrantLock和ReentrantReadWriteLock,它默认情况下是非公平锁,但是可以设置为公平锁。

4:读写锁:读写锁将对一个资源(比如文件)的访问分成了2个锁,一个读锁和一个写锁。ReadWriteLock就是读写锁,它是一个接口,ReentrantReadWriteLock实现了这个接口。

24.  写出生产者消费者模式。

生产者和消费者之间进行同步操作。用synchronized关键字和wait()/notifyAll()实现或用ReentrantLock锁和Condition变量来实现。

25.  ThreadLocal的设计理念与作用。

提供线程内部的局部变量,在本线程内随时随地可取,隔离其他线程。TheadLocal的创建方式ThreadLocal<Integer> local = new ThreadLocal<Integer>();

ThreadLocal中有一个静态内部类ThreadLocalMap(该map就理解为hashMap吧,只是改map中的enty实体继承了弱引用,也就是说,如果该Map中的对象太多的时候,即使我们没有主动放弃,可能垃圾回收器回收);同时在Thread类中有一ThreadLocalMap的组合,为ThreadLocal.ThreadLocalMap<ThreadLocal,Object>threadLocals;从这里可以看出,当在一个线程中调用local.set(100)方法的时候,则在该线程对象的ThreadLocalMap中存入<local,100>;当通过local.get()方法的时候,则先获取当前线程,通过当前线程获取ThreadLocalMap,然后,通过该local变量为key值去获取对应的value(100);

TheadLocal变量不是用来实现资源共享的,而是用来实现资源的在各个线程中的隔离。各个线程单独占有。虽然也可用局部变量,但是有些场景,如果说资源只有一个的时候,就得使用ThreadLcoal变量。还有要注意是对一个对象,如ThreadLocal<Person>这种状况,则map中存的是指向person的引用。要特别小心。

26.  ThreadPool用法与优势。

第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。但是要做到合理的利用线程池,必须对其原理了如指掌。用法详见http://blog.csdn.net/qq407388356/article/details/79551451。

27.  Concurrent包里的其他东西:ArrayBlockingQueueCountDownLatch等等。

BlockingQueue

如果BlockingQueue是空的,从BlockingQueue取东西的操作将会被阻断进入等待状态,直到BlockingQueue进了东西才会被唤醒,同样,如果BlockingQueue是满的,任何试图往里存东西的操作也会被阻断进入等待状态,直到BlockingQueue里有空间时才会被唤醒继续操作。 BlockingQueue有四个具体的实现类,

ArrayBlockingQueue:规定大小的BlockingQueue,其构造函数必须带一个int参数来指明其大小。其所含的对象是以FIFO(先入先出)顺序排序的。

LinkedBlockingQueue:大小不定的BlockingQueue,若其构造函数带一个规定大小的参数,生成的BlockingQueue有大小限制,若不带大小参数,所生成的BlockingQueue的大小由Integer.MAX_VALUE来决定。其所含的对象是以FIFO顺序排序的。

PriorityBlockingQueue:类似于LinkedBlockingQueue,但其所含对象的排序不是FIFO,而是依据对象的自然排序顺序或者是构造函数所带的Comparator决定的顺序。

SynchronousQueue:特殊的BlockingQueue,对其的操作必须是放和取交替完成的。

Semaphore

Java 并发库的Semaphore 可以很轻松完成信号量控制,Semaphore可以控制某个资源可被同时访问的个数,acquire()获取一个许可,如果没有就等待,而release()释放一个许可。比如在Windows下可以设置共享文件的最大客户端访问个数。

CountdownLatch

CountDownLatch的一个非常典型的应用场景是:有一个任务想要往下执行,但必须要等到其他的任务执行完毕后才可以继续往下执行。假如我们这个想要继续往下执行的任务调用一个CountDownLatch对象的await()方法,其他的任务执行完自己的任务后调用同一个CountDownLatch对象上的countDown()方法,这个调用await()方法的任务将一直阻塞等待,直到这个CountDownLatch对象的计数值减到0为止。(当每个线程调用countdown方法直到将countdownlatch方法创建时数减为0时,那么之前调用await()方法的线程才会继续执行。有一点注意,那就是只执行一次,不能到0以后重新执行)

CyclicBarrier

类有一个整数初始值,此值表示将在同一点同步的线程数量。当其中一个线程到达确定点,它会调用await() 方法来等待其他线程。当线程调用这个方法,CyclicBarrier阻塞线程进入休眠直到其他线程到达。当最后一个线程调用CyclicBarrier 类的await() 方法,它唤醒所有等待的线程并继续执行它们的任务。(当等待的线程数量达到CyclicBarrier线程指定的数量以后(调用await方法的线程数),才一起往下执行,否则大家都在等待,注意:如果达到指定的线程数量的时候:则可以重新计数,上面的过程可以循环)

CountDownLatch是减计数方式,计数==0时释放所有等待的线程;CyclicBarrier是加计数方式,计数达到构造方法中参数指定的值时释放所有等待的线程。

28.  wait()sleep()的区别。

调用sleep()和yield()时锁没有释放,但是在sleep的时候,可以调用interrupt()函数来中断该线程。调用wait()时,线程被挂起释放锁。

wait()是Object类中,与notify和notifyAll搭配使用,sleep()属于Thread类中的。

suspend将当前线程挂起,要等到线程调用resume()方法,该线程才能重新执行。

29.  foreach与正常for循环效率对比。

直接for循环效率最高,其次是迭代器和 ForEach操作。其实 ForEach 编译成字节码之后,使用的是迭代器实现的。比迭代器遍历多了生成中间变量这一步,因为性能也略微下降了一些。

在多线程中,则使用foreach,如果是在方法中读局部变量的操作,则使用for。

30.  Java IONIO

IO是面向流的,NIO是面向缓冲区的

IO的各种流是阻塞的,Java NIO的非阻塞模式

Java NIO的选择器允许一个单独的线程来监视多个输入通道

31.  反射的作用与原理。

动态加载类,在运行时load到内存中。反射机制的原理:反射就是java语言在运行时可以通过获取类的class对象,并通过class对象获取到当前对象的方法(包括私有方法),属性,以及构造方法,运行时class对象相当于外部程序访问内存中该类的一道门,通过该到门能获取到运行时该类的各种东西。

Spring中的IoC的实现原理就是工厂模式加反射机制。

32.  泛型常用特点,List<String>能否转为List<Object>

泛型的好处是在编译的时候检查类型安全(类型检查是保证类型转换是可以正确转换的(object编译的时候就没有检查到父类转子类)),并且所有的强制转换都是自动和隐式的,提高代码的重用率。编译之后都会擦除。List<String>不能转为List<Object>,因为泛型并不具有继承性。只是一个限定的作用。

33.  解析XML的几种方式的原理与特点:DOMSAXPULL

1:DOM是基于树的解析,DOM是把XML全部加载到内存中建立一棵树之后再进行处理。所以DOM不适合处理大型的XML【会产生内存的急剧膨胀】。这样可以随时修改xml文件内容。

2:SAX基于事件的解析,sax解析一行一行的读取xml文件开始标记的时候执行startElement方法,然后执行对应解析方法character方法,当遇到结束表示符就调用endElement方法,所以所是基于事件型解析, SAX不必把全部的xml都加载到内存中。但是SAX的缺点也很明显,它只能对文件顺序解析一遍,不支持对文件的随意存取。SAX也仅仅能够读取文件的内容,并不能修改内容。DOM可以随意修改文件树。(主要用于读取xml文件)

3:SAX 和 DOM 不是相互排斥的,记住这点很重要。可以使用 DOM 来创建 SAX 事件流,也可以使用 SAX 来创建 DOM 树。事实上,用于创建 DOM 树的大多数解析器实际上都使用 SAX 来完成。

34.  JavaC++对比。

(1)Java比C++程序可靠性更高。

(2)Java语言中没有指针的概念,

(3)Java用接口(Interface)技术取代C++程序中的多继承性;

(4)Java语言不需要程序对内存进行分配和回收。Java中自动回收内存,而c++中要调用new和delete来申请和释放内存,如果对内存分配与申请掌握不好就容易报错

(5)异常:JAVA中的异常机制用于捕获例外事件,增强系统容错能力 try{//可能产生例外的代码 }catch(exceptionTypename){ //处理 } 其中exceptionType表示异常类型。而C++则没有如此方便的机制

(6)Java不提供goto语句,指定goto作为关键字,但不支持它的使用,使程序简洁易读。

35.  Java1.71.8新特性。

1.7新特性:

1:switch中可以使用字串了运用

2:List<String> tempList = new ArrayList<>(); 即泛型实例化类型自动推断

3.语法上支持集合,而不一定是数组final List<Integer> piDigits = [ 1,2,3,4,5,8 ];

4:try块可以不用finally手动关闭,直接try块中就可以关闭Try-With-Resource语法

5:很长的数字可读性不好,在Java 7中可以使用下划线分隔长int以及long了,如1_1 * 10 = 110,120 – 1_0 = 110

Java1.8新特性

1: Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default关键字即可,default double sqrt(int a) {return Math.sqrt(a);},改方法不用实现,可以自己直接通过子类调用。

2:增加了lamda表达式

3:Java 8 在包java.time下包含了一组全新的时间日期API

36.  JNI的使用。

1:JNI(Java Native Interface)就是通过java本地方法可以调用其他语言的代码,能与其它语言(如C、C++)的动态库进行交互

2:JNI的使用场景

java是一门半解释性的语言,代码很容易被别人反编译,因此可以采用jni通过c语言或者c++来实现其核心,这样还能提高一定的性能;

一些性能要求特别高时,可以采用汇编语言实现,通过jni调用;

别的语言已经实现了的功能,通过jni可以直接调用,而不用重新编写。

jni使用步骤:用native关键字定义本地方法,然后编译该类为class文件,用javah工具生成.h的头文件,然后用c/c++语言实现其中的.h头文件中的方法,并生成动态库(以.so结尾的文件,大多数动态库是以.dll文件结尾的)),然后提供给java使用

37.  Java是否有内存泄露和内存溢出

静态集合类:由于他们的生命周期和应用程序一样长,不断添加对象时,可能会造成内存泄露。

监听器:增加监听器时,往往释放的时候没有删除对应的监听器。

物理连接:数据库和网络连接等,除非显示关闭了连接,否则不会被GC自动回收。当不用时必须用close()来释放,因为这些连接独立于JVM。一般在try块中建立连接,在finally里释放。

还有内部类和单例模式等,其实总结起来就是JVM对象是否可达{虚拟机栈(栈帧中的本地变量表)中引用的对象;方法区中类静态属性引用的对象;方法区中常量引用的对象;本地方法栈中JNI(Native方法)引用的对象}。

38.  Student s= new Student();在内存中做了哪些事

加载Student.class文件;在栈内为s开辟空间;在堆内为student对象开辟空间;对student成员变量进行默认初始化;对student成员变量进行显示初始化;通过构造方法对student对象成员变量赋值;student初始化完毕把对象引用赋值给s。

39.  持续更新。。。

有新的问题会继续更新。。。

猜你喜欢

转载自blog.csdn.net/qq407388356/article/details/79622218