JAVA面试问题—基础篇(下)

来源:互联网 发布:在linux中配置grub 编辑:程序博客网 时间:2024/06/05 06:37

1.Concurrent包

这个包包含有一系列能够让 Java 的并发编程变得更加简单轻松的类。

BlockingQueue:如果BlockingQueue是空的,获取操作将会被阻断进入等待状态,直到BlockingQueue有数据,同样,如果BlockingQueue是满的,添加操作会被阻断进入等待状态,直到BlockingQueue里有空间。
BlockingQueue有四个具体的实现类:

  • ArrayBlockingQueue:规定大小,FIFO,需要初始化大小
  • LinkedBlockingQueue:大小不定,FIFO,默认上限为Integer.MAX_VALUE
  • PriorityBlockingQueue:类似LinkedBlockingQueue,自然排序或Comparator决定
  • SynchronousQueue:存放须交替进行

Semaphore:专用于处理信号量问题,控制某个资源可被同时访问的个数,acquire()获取一个许可,如果没有就等待,而release()释放一个许可。

CountDownLatch:调用await()方法的任务将一直阻塞等待,直到这个CountDownLatch对象的计数值减到0为止。(不可循环)

CyclicBarrier:在某一点同步所有进程,先到达的线程会被await()直到其他线程到达。当最后一个线程调用CyclicBarrier 类的await() 方法,它唤醒所有等待的线程并继续执行它们的任务。(可循环)

2.wait()和sleep()的区别

wait方法是Object类中的方法,与notify/notifyAll配套使用,该方法不会持有获取的锁。
sleep方法是Thread类中的方法,指定时间后会自动结束,可以使用interrupt方法将其中断,该方法会持有获取的锁。

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

foreach在使用时会创建iterator方法,会不断调用hashNext方法和next方法读取数据,其中还要检查mod_count防止快速失败,故foreach是线程安全的,但也导致效率低下。适用于多线程。
for循环在使用时没有这些要求,循环速率快但线程不安全,适用于在方法中操作局部变量。

4.Java IO与NIO

JAVA IO:基于流,由于其每次只能获取有限字节,因此不能移动流中数据。使用IO流线程会阻塞直到数据读取或者写完毕,中间线程不能干别的事情
JAVA NIO:基于缓存,每次将所有数据读入缓存在,因此可以移动数据。NIO中线程可以通过通道去向缓存中发送一个读取数据请求,当没有数据时就会去做别的事,而不会一直等待直到读取到数据为止。写操作仍然会阻塞。
NIO的最重要的地方是当一个连接创建后,不需要对应一个线程,这个连接会被注册到多路复用器上面,所以所有的连接只需要一个线程就可以搞定,当这个线程中的多路复用器进行轮询的时候,发现连接上有请求的话,才开启一个线程进行处理,也就是一个请求一个线程模式。
NIO适用环境:如果需要管理同时打开的成千上万个连接,这些连接每次只是发送少量的数据,如聊天服务器。

5.反射的作用与原理

反射:在程序运行过程中,动态获取类的信息以及动态调用对象的方法的功能。

反射的作用:

  • 在运行时判断任意一个对象所属的类;
  • 在运行时构造任意一个类的对象;
  • 在运行时判断任意一个类所具有的成员变量和方法;
  • 在运行时调用任意一个对象的方法;
  • 生成动态代理 。

反射的好处:增加代码灵活性,使系统和插件解耦和(如Spring实现的控制反转(IOC))

其工作流程:运行时通过类的名称获取类的class对象,通过class对象获取当前对象的方法、属性、构造方法。

6.JAVA泛型

JAVA泛型的种类有三种,分别是泛型类、泛型接口、泛型方法。泛型安全简单,增加了代码复用性,解决了方法重载等问题。
泛型的主要体现是类型的安全检查与限定,确保所有类型可以正确转换,并且所有的强制转换都是自动和隐式的,不必为此修改代码。其次,泛型不具有继承性,主要作用是限定。如List不能转换成List。

7.XML文件的解析

DOM基于树的解析,将XML全部读取并在内存中建立为一棵树,所以不适合大型XML的解析,好处是可以随时修改文件内容。

SAX基于事件解析,按行读取文件并开始解析。不必一次性读取所有文件内容,但只能顺序解析文件且不支持文件的随机修改。

SAX 和 DOM 不是相互排斥的,可以使用 DOM 来创建 SAX 事件流,也可以使用 SAX 来创建 DOM 树。

8.JAVA与C++的对比

  • JAVA中不存在指针。
  • JAVA的内存管理自动申请和回收内存,程序员不做干涉。C++需要程序员手动new 和 delete。JAVA内存管理更为安全。
  • JAVA中不可以多继承,单使用接口代替此功能。
  • JAVA中的异常机制可以捕获例外事件,增强系统的容错性。try-catch-finally

9.设计模式

单例模式:单例模式是为确保一个类只有一个实例,并为整个系统提供一个全局访问点的一种模式方法。
饿汉、懒汉:是否在程序调用之前生成实例。
Double Check Locking 双检查锁机制

public class MySingleton {        //使用volatile关键字保其可见性      volatile private static MySingleton instance = null;       private MySingleton(){}      public static MySingleton getInstance() {          try {                if(instance != null){//懒汉式               }else{                  synchronized (MySingleton.class) {                      if(instance == null){//二次检查                          instance = new MySingleton();                      }                  }              }           } catch (InterruptedException e) {               e.printStackTrace();          }          return instance;      }  }  

工厂模式:

  • 简单工厂模式,该模式的工厂类一般是使用静态方法,通过接收的参数的不同来返回不同的对象实例。 不修改代码的话,是无法扩展的。
  • 工厂方法模式,该模式 对每一种产品提供一个工厂类 。通过不同的工厂实例来创建不同的产品实例。
    在同一等级结构中, 支持增加任意产品 。
  • 抽象工厂模式,该模式应对产品簇概念的。比如说,每个汽车公司可能要同时生产轿车,货车,客车,那么每一个工厂都要有创建轿车,货车和客车的方法。
    应对产品簇概念而生,增加新的产品线很容易,但是无法增加新的产品。
    和工厂方法的区别是,抽象工厂往往有多种方法,可以生产多种产品,即产品簇。

适配器:
类的适配器模式:把适配的类的API转换成为目标类的API。(继承)
模式所涉及的角色有:

  • 目标(Target)角色:这就是所期待得到的接口。注意:由于这里讨论的是类适配器模式,因此目标不可以是类。
  • 源(Adapee)角色:现在需要适配的接口。
  • 适配器(Adaper)角色:适配器类是本模式的核心。适配器把源接口转换成目标接口。显然,这一角色不可以是接口,而必须是具体类。

对象适配器模式:对象的适配器模式把被适配的类的API转换成为目标类的API,与类的适配器模式不同的是,对象的适配器模式不是使用继承关系连接到Adaptee类,而是使用委派关系连接到Adaptee类。(初始化)

10.动态代理与静态代理

代理模式的作用是:为其它对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介作用。三个角色,抽象角色、代理角色、真实角色。

实现的一般模式
  其实代理的一般模式就是静态代理的实现模式:首先创建一个接口(JDK代理都是面向接口的),然后创建具体实现类来实现这个接口,在创建一个代理类同样实现这个接口,不同指出在于,具体实现类的方法中需要将接口中定义的方法的业务逻辑功能实现,而代理类中的方法只要调用具体类中的对应方法即可,这样我们在需要使用接口中的某个方法的功能时直接调用代理类的方法即可,将具体的实现类隐藏在底层。
  
静态代理,在编译时就将接口、实现类、代理类一股脑儿全部手动完成,但如果我们需要很多的代理,每一个都这么手动的去创建实属浪费时间,而且会有大量的重复代码。

动态代理,动态代理的思维模式与之前的一般模式是一样的,也是面向接口进行编码,创建代理类将具体类隐藏解耦,不同之处在于代理类的创建时机不同,动态代理需要在运行时因需实时创建。利用反射机制来抽象出代理类的创建过程。需要implements InvocationHandler。
真正建立代理的代码

Iuser proxy = (Iuser) Proxy.newProxyInstance(Iuser.class.getClassLoader(), new Class[]{Iuser.class}, h);
原创粉丝点击