Java高频面试题(一)

Java高频面试题一


(根据自己的面试经验总结的知识点,内容比较浅显,有问题的地方欢迎指正呀)

一、 java的基础部分

1、简单讲一下java的跨平台原理

由于各操作系统(windows,liunx等)支持的指令集,不是完全一致的。就会让我们的程序在不同的操作系统上要执行不同程序代码。Java开发了适用于不同操作系统及位数的java虚拟机来屏蔽个系统之间的差异,提供统一的接口。对于我们java开发者而言,你只需要在不同的系统上安装对应的不同java虚拟机、这时你的java程序只要遵循java规范,就可以在所有的操作系统上面运行java程序了。
Java通过不同的系统、不同版本、不同位数的java虚拟机(jvm),来屏蔽不同的系统指令集差异而对外体统统一的接口(java API),对于我们普通的java开发者而言,只需要按照接口开发即可。如果我系统需要部署到不同的环境时,只需在系统上面按照对应版本的虚拟机即可。
​​

2、搭建一个java开发环境的步骤

Java开发环境需要些什么?

  1. 适用于我们开发环境的jdk
  2. 对应开发环境eclipse
  3. 还需要web服务器(tomcat)

一、下载对应组件
二、安装Jdk,安装正常流程安装即可,配置我们的JAVA_HOME,因为后面的eclispe和tomcat会依赖于这个变量.
Eclispe正常解压就ok,设置workspace的默认编码
Tomcat 正常解压就ok,把tomcat集成到eclispe中,安装插件就OK。

3、java中基本数据类型

在这里插入图片描述

4 、面向对象的基本特征

有四大基本特征:封装、抽象、继承、多态

  1. 封装性:即将对象封装成一个高度自治和相对封闭的个体,对象状态(属性)由这个对象自己的行为(方法)来读取和改变。张三这个人,他的姓名等属性,要有自己提供的获取或改变的方法来操作。private name setName getName
  2. 抽象:就是找出一些事物的相似和共性之处,然后将这些事物归为一个类,这个类只考虑这些事物的相似和共性之处,并且会忽略与当前主题和目标无关的那些方面,将注意力集中在与当前目标有关的方面。就是把现实生活中的对象,抽象为类。
  3. 继承:在定义和实现一个类的时候,可以在一个已经存在的类的基础之上来进行,把这个已经存在的类所定义的内容作为自己的内容,并可以加入若干新的内容,或修改原来的方法使之更适合特殊的需要,这就是继承。
  4. 多态:是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。

4.1、多态的前提?

1)要有继承关系或实现关系(接口);
2)要有方法重写;
3)要有父类或者父接口引用指向子类`Fu f= new Zi(); 注意:形参实参形式的(形参父类实参子类)。

4.2、多态的分类?

1)具体类多态
class Fu{}
class Zi extends Fu{}
Fu f= new Zi()//父类引用指向子类
2)抽象类多态(常用)
abstract class Fu{}
class Zi extends Fu{}
Fu f= new Zi();//抽象父类引用指向子类
3)接口多态(常用)
interface Fu{}
class Zi implements Fu{}
Fu f= new Zi();//父接口引用指向子类

4.3、多态的好处?

多态就是“一种接口,可以进行多种实现”
提高了代码的维护性(继承保证);提高了代码的扩展性。

5、有了基本数据类型,为什么还需要包装类型?

  1. 基本数据类型:java中提供了8中基本的数据类型。boolean int float等
  2. 包装类型:每一个基本的数据类型都会一一对应一个包装类型。
    boolean ----->Boolean、Int -------->Integer
  3. 装箱和拆箱
    装箱:把基本的数据类型转换成对应的包装类型.
    Integer .valueOf(1)
    Integer i = 1;自动装箱,实际上在编译时会调用Integer .valueOf方法来装箱
    拆箱:就是把包装类型转换为基本数据类型.基本数据类型 名称 = 对应的包装类型。
    Integer i = 1;
    int j = i;//自动拆箱//int j = i=intValue();手动拆箱
    自动拆箱:实际上会在编译调用intValue
    Java是一个面向对象的语言,而基本的数据类型,不具备面向对象的特性。
    null Integer—>null int---->0 用Integer和int分别表示Person这个类的ID,Max 最大值,min 最小值
    缓存值:对象缓存,Integer i=1; integer j= 1;i ==j

6、说一下"=="和equals方法究竟有什么区别?

非常经典的一个面试题?先说清楚一个,再来说另一个?
(1)==用来判断两个变量之间的的值是否相等。变量就可以分为基本数据类型变量,引用类型。
(2)如果是基本数据类型的变量直接比较值,而引用类型要比较对应的引用的内存的首地址。
​​在这里插入图片描述
equals 用来比较两个对象长得是否一样。判断两个对象的某些特征是否一样。实际上就是调用对象的equals方法进行比较。

7、讲一下String和StringBuilder的区别(final)?StringBuffer和StringBuilder的区别?

在java中提供三个类String(底层是不可变的字符串) 、StringBuillder(底层:线程不安全的字符串,初始16字节) 、StringBuffer(底层:线程安全的字符串,初始16字节)来表示和操作字符串。字符串就是多个字符的集合。

  1. String是内容不可变的字符串。String底层使用了一个不可变的字符数组(final char[])
    ​​在这里插入图片描述
    String str =new String(“bbbb”);
    StringBuillder StringBuffer,是内容可以改变的字符串。StringBuillder StringBuffer底层使用的可变的字符数组(没有使用final来修饰)

  2. 最经典就是拼接字符串。
    String进行拼接.String c = “a”+”b”;
    StringBuilder或者StringBuffer
    StringBuilder sb = new StringBuilder(); sb.apend(“a”).apend(“b”)

  3. 主要的区别如下:
    (1)运行速度:StringBuilder > StringBuffer > String , String是字符串常量,其他两者属于字符串变量,String对象创建后不可以改变,对String的操作实际上是不断创建和回收的过程,执行速度慢,拼接字符串不能使用String进行拼接,要使用StringBuilder或者StringBuffer。
    (2)线程安全:StringBuilder是线程不安全的,而StringBuffer是线程安全的。StringBuffer对象在字符串缓冲区被多个线程使用时,可以用synchronized关键字,能够保证线程安全。
    (3)使用场景:String适用于少量的字符串操作;StringBuilder适用于单线程下的在字符串缓冲区进行大量的操作的情况下,如JSON的封装等;StringBuffer适用于多线程下的在字符串缓冲区进行大量的操作的情况下,如HTTP参数解析和封装等。

8、讲一下java中的集合?

在这里插入图片描述
Java中的集合分为**value,key–vale(Collection Map)**两种。存储值有分为List 和Set。List是有序的,可以重复的。Set是无序的,不可以重复的。根据equals和hashcode判断,也就是如果一个对象要存储在Set中,必须重写equals和hashCode方法。存储key-value的为map

8.1、数组和集合的区别

  1. 长度的区别:数组的长度固定、集合的长度可变
  2. 内容不容:数组存储的是同一种类型的元素、集合可以存储不同类型的元素(但是一般我们不这样干…)
  3. 元素的数据类型:数组可以存储基本数据类型,也可以存储引用类型、集合只能存储引用类型(你存储的是简单的int,它会自动装箱成Integer)

8.2、讲一下HashMap哈HashTable的区别?HashTable和ConcurrentHashMap的区别?

  1. HashMap:采用(数组+链表)散列表+红黑树实现:HashMap的初始容量为16字节,Hashtable初始容量为11字节,两者的填充因子默认都是0.75。
    //默认初始容量为16字节//最大容量为2^ 31
    //当桶中元素个数小于6的时候转为链表,桶中元素个数大于8的时候且(散列表容量大于64)桶个数达到64时进行树化(数组元素称为桶)
  2. HashTable类:继承自Dictionary类,分别实现了Map、Cloneable、Java.io.Serializable

相同点:

  1. HashMap和HasheTalbe都可以使用来存储key–value的数据。
    HashMap扩容时是当前容量翻倍即:capacity2,Hashtable扩容时是容量翻倍+1即:capacity2+1。
    区别:
  2. HashMap是可以把null作为key或者value的,而HashTable是不可以的。
  3. HashMap是线程不安全的,效率较高。而HashTalbe是线程安全的,效率较低。

我想线程安全但是我又想效率高?
通过把整个Map分为N个Segment(类似HashTable),可以提供相同的线程安全,但是效率提升N倍,默认提升16倍。

8.3、ConcurrentHashMap和Hashtable的区别

它们都可以用于多线程的环境,但是当Hashtable的大小增加到一定的时候,性能会急剧下降,因为迭代时需要被锁定很长的时间。因为jdk1.7中ConcurrentHashMap引入了分割(segmentation),不论它变得多么大,仅仅需要锁定map的某个部分,而其它的线程不需要等到迭代完成才能访问map。简而言之,在迭代的过程中,ConcurrentHashMap仅仅锁定map的某个部分,而Hashtable则会锁定整个map。

8.4、HashMap遍历的几种方式

  1. 第一种:二次取值
    System.out.println(“通过Map.keySet遍历key和value:”);
    for (String key : map.keySet()) {
    System.out.println("key= "+ key + " and value= " + map.get(key));
  2. 第二种
    System.out.println(“通过Map.entrySet使用iterator遍历key和value:”);
    Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
    while (it.hasNext()) {
    Map.Entry<String, String> entry = it.next();
    System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
  3. 第三种:推荐,尤其是容量大时
    System.out.println("通过Map.entrySet遍历key和value");
    for (Map.Entry<String, String> entry : map.entrySet()) {
    System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
  4. 第四种:
    System.out.println(“通过Map.values()遍历所有的value,但不能遍历key”);
    for (String v : map.values()) {
    System.out.println("value= " + v);

8.5、哪些集合类是线程安全的?

Vector、HashTable、Properties和Stack是同步类,所以它们是线程安全的,可以在多线程环境下使用。Java1.5并发API包括一些集合类,允许迭代时修改,因为它们都工作在集合的克隆上,所以它们在多线程环境中是安全的。

8.6、HashMap还是TreeMap使用场景

对于在Map中插入、删除和定位元素这类操作,HashMap是最好的选择。
然而,假如你需要对一个有序的key集合进行遍历,TreeMap是更好的选择。基于你的collection的大小,也许向HashMap中添加元素会更快,将map换为TreeMap进行有序key的遍历。

8.7、ArrayList和Vector有何异同点?

ArrayList和Vector在很多时候都很类似。
相同点:
(1)两者都是基于索引的,内部由一个数组支持。(2)两者维护插入的顺序,我们可以根据插入顺序来获取元素。(3)ArrayList和Vector的迭代器实现都是fail-fast的。(4)ArrayList和Vector两者允许null值,也可以使用索引值对元素进行随机访问。
不同点:
(1)Vector是同步的,而ArrayList不是。然而,如果你寻求在迭代的时候对列表进行改变,你应该使用CopyOnWriteArrayList。(2)ArrayList比Vector快,它因为有同步,不会过载。(3)ArrayList更加通用,因为我们可以使用Collections工具类轻易地获取同步列表和只读列表。

8.8、什么时候更适合用Array?

Array可以容纳基本类型和对象,而ArrayList只能容纳对象。Array是指定大小的,而ArrayList大小是固定的。Array没有提供ArrayList那么多功能,比如addAll、removeAll和iterator等。尽管ArrayList明显是更好的选择,但也有些时候Array比较好用。(1)如果列表的大小已经指定,大部分情况下是存储和遍历它们。(2)对于遍历基本数据类型,尽管Collections使用自动装箱来减轻编码任务,在指定大小的基本类型的列表上工作也会变得很慢。(3)如果你要使用多维数组,使用[][]比List<List<>>更容易。

8.9、ArrayList和LinkedList有何区别?

ArrayList底层使用时数组。LinkedList使用的是链表。
数组查询具有所有查询特定元素比较快。而插入和删除和修改比较慢(数组在内存中是一块连续的内存,如果插入或删除是需要移动内存)。
链表不要求内存是连续的,在当前元素中存放下一个或上一个元素的地址。查询时需要从头部开始,一个一个的找。所以查询效率低。插入时不需要移动内存,只需改变引用指向即可。所以插入或者删除的效率高。
ArrayList使用在查询比较多,但是插入和删除比较少的情况,而LinkedList使用在查询比较少而插入和删除比较多的情况。
两者都实现了List接口,但是它们之间有些不同。
1)ArrayList是由Array所支持的基于一个索引的数据结构,所以它提供对元素的随机访问,复杂度为O(1),但LinkedList存储一系列的节点数据,每个节点都与前一个和下一个节点相连接。所以,尽管有使用索引获取元素的方法,内部实现是从起始点开始遍历,遍历到索引的节点然后返回元素,时间复杂度为O(n),比ArrayList要慢。
2)与ArrayList相比,在LinkedList中插入、添加和删除一个元素会更快,因为在一个元素被插入到中间的时候,不会涉及改变数组的大小,或更新索引。
3)LinkedList比ArrayList消耗更多的内存,因为LinkedList中的每个节点存储了前后节点的引用。

8.10、哪些集合类提供对元素的随机访问?

ArrayList、HashMap、TreeMap和HashTable类提供对元素的随机访问。

9、使用字节流还是字符流?

(1)什么是字节流,什么是字符流?
字节流:传递的是字节(二进制),
字符流:传递的是字符
(2)他们各自的特点
使用字节流的传输的文件,不一定能使用字符流传输,使用字符流传输的文件都可以转成字节流再进行传输。音乐媒体等使用 的是字节,使用字符无法传输。我们拷贝的文件不确定只包含字符流,有可能包含字节流(图片,视频,音乐)。考虑到通用性,所以要使用字节流。

10、简单讲一下类加载机制?

:类的加载指的是类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区中创建一个java.lang.Class对象,用来封装方法区中的数据结构。
包括五个阶段:加载、验证、准备、解析、初始化

11、内存泄漏以及解决办法

造成的后果:频繁GC、程序崩溃
1、单例造成的内存泄漏:由于单例的静态特性使得其生命周期和应用的生命周期一样长,如果一个对象已经不再需要使用了,而单例对象还持有该对象的引用,就会使得该对象不能被正常回收,从而导致了内存泄漏。
2、 非静态内部类创建静态实例造成的内存泄漏
3、 Handler造成的内存泄漏示例:创建匿名内部类的静态对象
4、 线程造成的内存泄漏
5、 资源未关闭造成的内存泄漏:对于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,从而造成内存泄漏。
6、使用ListView时造成的内存泄漏:解决方法:在构造Adapter时,使用缓存的convertView。
7、集合容器中的内存泄露
我们通常把一些对象的引用加入到了集合容器(比如ArrayList)中,当我们不需要该对象时,并没有把它的引用从集合中清理掉,这样这个集合就会越来越大。如果这个集合是static的话,那情况就更严重了。
解决方法:在退出程序之前,将集合里的东西clear,然后置为null,再退出程序。
8、WebView造成的泄露:当我们不要使用WebView对象时,应该调用它的destory()函数来销毁它,并释放其占用的内存,否则其长期占用的内存也不能被回收,从而造成内存泄露。
解决方法:为WebView另外开启一个进程,通过AIDL与主线程进行通信,WebView所在的进程可以根据业务的需要选择合适的时机进行销毁,从而达到内存的完整释放。

12、如何避免内存泄漏?

(1)在涉及使用Context时,对于生命周期比Activity长的对象应该使用Application的Context。凡是使用Context优先考虑Application的Context,当然它并不是万能的,对于有些地方则必须使用Activity的Context。
(2)对于需要在静态内部类中使用非静态外部成员变量(如:Context、View ),可以在静态内部类中使用弱引用来引用外部类的变量来避免内存泄漏。
(3)对于不再需要使用的对象,显示的将其赋值为null,比如使用完Bitmap后先调用recycle(),再赋为null。
(4)保持对对象生命周期的敏感,特别注意单例、静态对象、全局性集合等的生命周期。
(5)对于生命周期比Activity长的内部类对象,并且内部类中使用了外部类的成员变量,可以这样做避免内存泄漏:1)将内部类改为静态内部类、2)静态内部类中使用弱引用来引用外部类的成员变量

13、内存溢出的原因及其解决办法:

指的是申请内存时,没有足够的内存供申请者使用,或者说,就会报错OOM,即所谓的内存泄露
原因:
1、内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
2、集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
3、代码中存在死循环或循环产生过多重复的对象实体;
4、使用的第三方软件中的BUG;
5、启动参数内存值设定的过小、
解决办法:
1、修改JVM启动参数,直接增加内存。(-Xms,-Xmx参数一定不要忘记加。
2、检查错误日志,查看“OutOfMemory”错误前是否有其 它异常或错误
3、对代码进行走查和分析,找出可能发生内存溢出的位置

14、Lilux常用命令

Cd跳转到目录
Pwd 获取当前路径,显示当前工作目录的路径
Su 切换用户
Ll 查看当前目录下文件和目录
Tail 查看文件
Rm –rf 删除文件
Vi 修改文件
Mv 移动/重命名 文件或文件夹
Mkdir 创建文件夹
Rm –f 删除文件夹
Tar 打包/解压
Grep 查找想要的信息(进程、目录之类的)
kill、pkill、killall都是终止进程
top实时查看进程的状态:

14.1、Lilux目录中的权限

r(Read,读取):对文件而言,具有读取文件内容的权限;对目录来说,具有浏览目 录的权限。 w(Write,写入):对文件而言,具有新增、修改文件内容的权限;对目录来说,具有删除、移动目录内文件的权限。x(eXecute,执行):对文件而言,具有执行文件的权限;对目录了来说该用户具有进入目录的权限。
​​在这里插入图片描述
文件的类型:
d:代表目录 -:代表文件 l:代表链接(可以认为是window中的快捷方式)
后面的9位分为3组,每3位置一组,分别代表属主的权限,与当前用户同组的用户的权限,其他用户的权限
r:代表权限是可读,r也可以用数字4表示
w:代表权限是可写,w也可以用数字2表示
x:代表权限是可执行,x也可以用数字1表示
2.1.17.2、linux有多少个进程的指令(8个)

使用 which 命令查看可执行文件的完整路径。

15、几种排序算法

​​在这里插入图片描述

16、Object类中有哪些方法?

1 registerNatives() //私有方法
2 getClass() //返回此 Object 的运行类。
3 hashCode() //用于获取对象的哈希值。
4 equals(Object obj) //用于确认两个对象是否“相同”。
5 clone() //创建并返回此对象的一个副本。
6 toString() //返回该对象的字符串表示。
7 notify() //唤醒在此对象监视器上等待的单个线程。
8 notifyAll() //唤醒在此对象监视器上等待的所有线程。
9 wait(long timeout) //在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。
10 wait(long timeout, int nanos) //在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量前,导致当前线程等待。
11 wait() //用于让当前线程失去操作权限,当前线程进入等待序列
12 finalize() //当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。

17、Java中几种引用类型?

(1) 强引用: 如果一个对象具有强引用的话,垃圾回收器不会回收这个对象,即使内存空间不足的时候也不会回收具有强引用类型的对象,平时通过new创建一个对象赋值给一个变量(A a = new A()),这种方式就是强引用,强引用类型的对象,在不使用时要赋值为null,才能被垃圾回收器回收。
(2) 软引用: 如果一个对象只具有软引用,内存空间足够,垃圾回收器就不会回收它,如果内存空间不足,就会回收这些对象的内存。只要垃圾回收器没有回收软引用类型的对象,程序就一直可以使用该对象。适用于网页缓存、图片缓存,防止内存溢出,在内存充足的时候,缓存对象会一直存在,在内存不足的时候,缓存对象占用的内存会被垃圾收集器回收。
(3) 弱引用:弱引用(WeakReference),具有弱引用的对象的生命周期更短,垃圾回收器在扫描的时候,一旦发现只具有弱引用的对象,不管内存空间是否足够,都会回收它的内存。
(4) 虚引用:虚引用并不会决定对象的生命周期,如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收

18、runTime出现的场景?

  1. 除以零
  2. 数组越界:int a[3]; a[10000000]=10;
  3. 指针越界:int * p; p=(int *)malloc(5 * sizeof(int)); *(p+1000000)=10;
  4. 使用已经释放的空间:int * p; p=(int *)malloc(5 * sizeof(int));free§; *p=10;
  5. 数组开得太大,超出了栈的范围,造成栈溢出:int a[100000000];

19、final和finally、finalize的区别?

(1) final:用于声明属性、方法和类,分别表示属性不可变(引用不可变)、方法不可覆盖、类不可被继承
(2) fianlly:作为异常处理的一部分,只能用在try-catch语句当中,并且附带一个语句块(一定注意:语句块不一定会被执行!!),经常被用在需要释放资源的情况下
(3) finalize:是在java.lang.Object里定义的,也就是说每一个对象都有这么个方法。这个方法在gc启动,该对象被回收的时候被调用。其实gc可以回收大部分的对象(凡是new出来的对象,gc都能搞定,一般情况下我们又不会用new以外的方式去创建对象),所以一般是不需要程序员去实现finalize的。 特殊情况下,需要程序员实现finalize,当对象被回收的时候释放一些资源,比如:一个socket链接,在对象初始化时创建,整个生命周期内有效,那么就需要实现finalize,关闭这个链接。 使用finalize还需要注意一个事,调用super.finalize();一个对象的finalize()方法只会被调用一次,而且finalize()被调用不意味着gc会立即回收该对象,所以有可能调用finalize()后,该对象又不需要被回收了,然后到了真正要被回收的时候,因为前面调用过一次,所以不会调用finalize(),产生问题。 所以,推荐不要使用finalize()方法,它跟析构函数不一样。

19.1、finally一定会被执行吗?

(1) 只有与finally对应的try语句得到执行的情况下,finally才会被执行;
(2) 如果try中执行了System.out.exit(),会终止jvm的运行,finally也不会被执行
(3) 当一个线程被打断的时候,也不会被执行

20、IO和NIO

​​在这里插入图片描述
Java NIO: 单线程管理多个连接
1. 面向流与面向缓冲
Java IO和NIO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。 Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。 Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。
2. 阻塞与非阻塞IO
Java IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取,而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。
io的各种流是阻塞的,就是当一个线程调用读写方法时,该线程会被阻塞,直到读写完,在这期间该线程不能干其他事,CPU转而去处理其他线程,假如一个线程监听一个端口,一天只会有几次请求进来,但是CPU却不得不为该线程不断的做上下文切换,并且大部分切换以阻塞告终。
NIO通讯是将整个任务切换成许多小任务,由一个线程负责处理所有io事件,并负责分发。它是利用事件驱动机制,而不是监听机制,事件到的时候再触发。NIO线程之间通过wait,notify等方式通讯。保证了每次上下文切换都有意义,减少无谓的进程切换。
3. 选择器(Selectors)
Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。

21、什么是微服务?

微服务就是将一个单体架构的应用按业务划分为一个个的独立运行的程序即服务,它们之间通过HTTP协议进行通信(也可以采用消息队列来通信,如RoocketMQ,Kafaka等),可以采用不同的编程语言,使用不同的存储技术,自动化部署(如Jenkins)减少人为控制,降低出错概率。服务数量越多,管理起来越复杂,因此采用集中化管理。例如Eureka,Zookeeper等都是比较常见的服务集中化管理框架。

21.1、微服务的优势

1)将复杂的业务拆分成多个小的业务,每个业务拆分成一个服务,将复杂的问题简单化。利于分工,降低新人的学习成本。
2)微服务系统是分布式系统,业务与业务之间完全解耦,随着业务的增加可以根据业务再拆分,具有极强的横向扩展能力。面对搞并发的场景可以将服务集群化部署,加强系统负载能力。
3)服务间采用HTTP协议通信,服务与服务之间完全独立。每个服务可以根据业务场景选取合适的编程语言和数据库。
4)微服务每个服务都是独立部署的,每个服务的修改和部署对其他服务没有影响。

21.2、微服务和SOA的关系

SOA即面向服务的架构,SOA是根据企业服务总线(ESB)模式来整合集成大量单一庞大的系统,微服务可以说是SOA的一种实现,将复杂的业务组件化。但它比ESB实现的SOA更加的轻便敏捷和简单。

22、接口和异常

22.1、接口

1. 接口定义:
接口是抽象方法的集合,是一个规范,一般不去实现方法内容,接口的关键字是interface,需要有子类去实现接口中的所有方法。一个类可以实现多个接口(Java中的多继承)
2. 定义一个接口的步骤:
(1)创建一个接口,关键字为interface;
(2)书写接口中的方法和属性,方法不能有方法体且访问修饰符必须是public(可以省略);
(3)创建一个类。通过关键字implements实现接口,如果有多个接口,可以用逗号隔开实现多个接口;
(4)这个类必须实现接口中的所有方法,如果没有实现所有方法,那么这个类要定义成抽象类
3. 注意事项:
(1)接口中方法的访问修饰符必须是public,public可以省略;
(2)接口中的方法默认使用public abstract来修饰;
(3)接口中的变量默认使用public static final修饰,所以属性必须赋值。接口是存放常量的好地方。
(4)接口不能被new,接口中没有构造器,仅仅是一个规范而已;
(5)多个接口如果定义了相同名称,参数列表的方法,返回类型必须相同。
(6)接口可以继承。

23、异常处理

23.1、概念:

(1)异常概念:程序在执行过程中出现不正常现象,异常在Java中最大的父类是Exception
(2)错误概念:程序在执行过程中,系统、硬件等出现了故障,程序员无法解决处理的情况。错误在Java中最大的父类是Error
(3)异常的错误有一个共同的父类Throwable
(4)异常分为运行时异常(RuntimeException)和编译时异常,运行时异常的父类是RuntimeException,除了RuntimeException和他的子类,其他类都属于编译时异常;

23.2、常见异常

(1)空指针异常:java.lang.NullPointerException
调用了未经初始化的对象或者不存在的对象
(2)数组下标越界:java.lang.ArrayIndexOutBoundsException,对数组操作时一定要认真检查,看下标是否超出数组范围
(3)数学运算异常:java.lang.ArithmeticException
(4)未找到路径异常:java.lang.ClassBotFoundException

23.3、异常处理

(1)抓异常(try、catch、finally)
a)情况一:代码有可能出现异常,但是并没有真正出现异常,那么程序按照规定路线进行,不执行catch内容;
b)情况二:代码有可能出现异常,结果也真正出现了异常,程序进入catch进行异常处理;
c)情况三:代码有肯可能出现异常,也进行了异常处理后程序还是出现了异常,原因是处理的异常和出现的异常不匹配;
(2)异常处理需要考虑周到;
(3)有多个异常时的处理:
书写多个catch块,前边的catch进入后,后边的catch就不会执行;书写的catch块的顺序为先子类后父类(先具体后通用)

23.4、注意事项:

(1)try:有可能出现异常的代码块,写在第一位只能写一个try(可以嵌套写)
(2)catch:对异常进行处理的代码,需要与出现的异常匹配上才进行处理。catch可以写一个或多个也可以不写,如果不写的话必须要写finally。
(3)finally:不管发生任何情况,都会执行的代码块,书写在最后,要么不写要么写一个;
(4)如果catch代码块最后写了return,后面有finally语句,则不会执行finally;
5、抛异常
(1)Java中允许在方法的后面使用throws关键字对外声明该方法有可能会发生异常,这样调用者在调用该方法时必须对异常进行处理。否则编译无法通过。throws后面声明方法中发生异常的类型
(2)thorw关键字用于在方法中声明抛出异常的实例对象

24、Java反射和类加载机制?

24.1、类的加载机制

jvm把class文件加载到内存,并对数据进行校验、解析和初始化,最终形成jvm可以直接使用的java类型的过程。

  1. 加载: 将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区中的运行时数据结构,在堆中生成一个代表这个类的java.lang.Class对象,作为方法区类数据的访问入口。
  2. 链接 :将java类的二进制代码合并到jvm的运行状态之中的过程。
  3. **验证:**确保加载的类信息符合jvm规范,没有安全方面的问题。
  4. **准备:**正式为类变量(static变量)分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配。
  5. **解析:**虚拟机常量池内的符号引用替换为直接引用的过程。(比如String s =“aaa”,转化为 s的地址指向“aaa”的地址)
  6. **初始化:**初始化阶段是执行类构造器方法的过程。类构造器方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static块)中的语句合并产生的。当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先初始化其父类的初始化。虚拟机会保证一个类的构造器方法在多线程环境中被正确加锁和同步。当访问一个java类的静态域时,只有真正声明这个静态变量的类才会被初始化。

24.2、java反射机制

通俗地说:反射就是将Student类中的方法、成员变量、构造方法,抽象出来可以让Class的对象单独进行访问。JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

25、获取Class对象的三种方式

(1)通过调用forName(String className),注意className的格式为:包名.类名获得Class对象。
(2)通过类获取Class对象,String.class、int.class这些字节码是Class类的对象。
(3)通过类的对象获取Class对象。

26、获取构造方法

(1)获取公开的构造方法
(2)获取所有的构造方法: 主要为调取Student类中的私有方法。注意:不能获得Student类的父类Human中的构造方法。
(3)调用特定的构造方法:无参构造和有参构造方法Constructors1.newInstance();//实例化获取的类。 私有构造方法的调用,要注意要设置其访问权限Constructors1.setAccessible(true)。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值