【JAVA面试的艺术】JAVA基础知识阶段三

145 阅读7分钟

说一下线程的生命周期?

线程是一个动态执行的过程,它也有一个从产生到死亡的过程,有五种状态。

  • 新建(new Thread)
    • 当创建Thread类的一个实例(对象)时,此线程进入新建状态(未被启动)。例如:Thread t1=new Thread();
  • 就绪(runnable)
    • 线程已经被启动,正在等待被分配给CPU时间片,也就是说此时线程正在就绪队列中排队等候得到CPU资源。例如:t1.start();
  • 运行(running)
    • 线程获得CPU资源正在执行任务(run()方法),此时除非此线程自动放弃CPU资源或者有优先级更高的线程进入,线程将一直运行到结束。
  • 死亡(dead)
    • 当线程执行完毕或被其它线程杀死,线程就进入死亡状态,这时线程不可能再进入就绪状态等待执行。
    • 自然终止:正常运行run()方法后终止
    • 异常终止:调用stop()方法让一个线程终止运行
  • 阻塞(blocked)
    • 阻塞状态是指线程因为某种原因放弃了 cpu 使用权,也即让出了 cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得 cpu timeslice 转到运行(running)状态。阻塞的情况分三种:
      • 等待阻塞 ( o.wait-> 等待对列:运行(running)的线程执行 o.wait()方法,JVM 会把该线程放入等待队列(waitting queue)中。
      • 同步阻塞 (lock-> 锁池 ):运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则 JVM 会把该线程放入锁池(lock pool)中。
      • 其他阻塞 (sleep/join):运行(running)的线程执行 Thread.sleep(long ms)或 t.join()方法,或者发出了 I/O 请求时,JVM 会把该线程置为阻塞状态。当 sleep()状态超时、join()等待线程终止或者超时、或者 I/O处理完毕时,线程重新转入可运行(runnable)状态。

sleep()和wait()有什么区别吗?

  • sleep()方法是线程类(Thread)的静态方法,让调用线程进入睡眠状态,让出执行机会给其他线程,等到休眠时间结束后,线程进入就绪状态和其他线程一起竞争cpu的执行时间
  • 因为sleep() 是static静态的方法,他不能改变对象的锁,当一个synchronized块中调用了sleep() 方法,线程虽然进入休眠,但是对象的机锁没有被释放,其他线程依然无法访问这个对象。
  • wait()是Object类的方法,当一个线程执行到wait方法时,它就进入到一个和该对象相关的等待池,同时释放对象的锁,使得其他线程能够访问,可以通过notify,notifyAll方法来唤醒等待的线程。

简述基于TCP和UDP的Socket编程主要步骤

  1. Java分别为TCP和UDP 两种通信协议提供了相应的Socket编程类,这些类存放在java.net包中。与TCP对应的是服务器的ServerSocket和客户端的Socket,与UDP对应的是DatagramSocket。

  2. 基于TCP创建的套接字可以叫做流套接字,服务器端相当于一个监听器,用来监听端口。 服务器与客服端之间的通讯都是输入输出流来实现的。基于UDP的套接字就是数据报套接字,• 两个都要先构造好相应的数据包。

  • 基于TCP协议的Socket编程的主要步骤

    • 服务器端(server)
      • 构建一个ServerSocket实例,指定本地的端口。这个socket就是用来监听指定端口的连接请求的。
      • 重复如下几个步骤:
        • 调用socket的accept()方法来获得下面客户端的连接请求。通过accept()方法返回的socket实例,建立了一个和客户端的新连接
        • 通过这个返回的socket实例获取InputStream和OutputStream,可以通过这两个stream来分别读和写数据
        • 结束的时候调用socket实例的close()方法关闭socket连接
    • 客户端(client)
      • 构建Socket实例,通过指定的远程服务器地址和端口来建立连接
      • 通过Socket实例包含的InputStream和OutputStream来进行数据的读写
      • 操作结束后调用socket实例的close方法,关闭
  • 基于UDP协议的Socket编程的主要步骤

    • 服务器端(server)
      • 构造DatagramSocket实例,指定本地端口。
      • 通过DatagramSocket实例的receive方法接收DatagramPacket.DatagramPacket中间就包含了通信的内容
      • 通过DatagramSocket的send和receive方法来收和发DatagramPacket
    • 客户端(client)
      • 构造DatagramSocket实例
      • 通过DatagramSocket实例的send和receive方法发送DatagramPacket报文
      • 结束后,调用DatagramSocket的close方法关闭

反射机制的优缺点

  1. 静态编译:在编译时确定类型,绑定对象,即通过。

  2. 动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。

  • 优点:可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发中,它的灵活性就表现的十分明显。

  • 缺点:对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。

HashMap和Hashtable的区别和联系

  • 实现原理相同,功能相同,底层都是哈希表结构,查询速度快,在很多情况下可以互用

  • 两者的主要区别如下

  1. Hashtable是早期JDK提供的接口,HashMap是新版JDK提供的接口

  2. Hashtable继承Dictionary类,HashMap实现Map接口

  3. Hashtable线程安全,HashMap线程非安全

  4. Hashtable不允许null值,HashMap允许null值

  5. Hashtable、HashMap都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式 。

  6. 初始容量大小和每次扩充容量大小的不同 Hashtable默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。

  7. 哈希值的使用不同,HashTable直接使用对象的hashCode。而HashMap重新计算hash值。

    • hashCode是jdk根据对象的地址或者字符串或者数字算出来的int类型的数值。

    • Hashtable计算hash值,直接用key的hashCode(),而HashMap重新计算了key的hash值,Hashtable在求hash值对应的位置索引时,用取模运算,而HashMap在求位置索引时,则用与运算,且这里一般先用hash&0x7FFFFFFF后,再对length取模,&0x7FFFFFFF的目的是为了将负的hash值转化为正值,因为hash值有可能为负数,而&0x7FFFFFFF后,只有符号外改变,而后面的位都不变。

字符流字节流联系区别;什么时候使用字节流和字符流?

  • 字符流和字节流是流的一种划分,按处理照流的数据单位进行的划分。两类都分为输入和输出操作。
  • 在字节流中输出数据主要是使用OutputStream完成,输入使的是InputStream,在字符流中输出主要是使用Writer类完成,输入流主要使用Reader类完成。这四个都是抽象类。
  • 字符流处理的单元为2个字节的Unicode字符,分别操作字符、字符数组或字符串,而字节流处理单元为1个字节,操作字节和字节数组。字节流是最基本的,所有的InputStrem和OutputStream的子类都是,主要用在处理二进制数据,它是按字节来处理的 但实际中很多的数据是文本,又提出了字符流的概念,它是按虚拟机的编码来处理,也就是要进行字符集的转化 这两个之间通过 InputStreamReader,OutputStreamWriter来关联,实际上是通过byte[]和String来关联的。

青蛙跳台阶青蛙可以一次跳1级/2级台阶请问跳上第n级台阶有多少种方法?

  • 递归:
if(target == 1){
​        return 1;
​    }
​    if(target == 2){
​        return 2;
​    }
​    //第一次有两种选择,然后根据不同的选择,然后开始不同的下一步,但是下一步还是一样有两种选择
​    return JumpFloor(target - 1) + JumpFloor(target - 2);
  • 非递归:
int x=1, y=2, z;
​        if(target == 1){
​            return 1;
​        }
​        if(target == 2){
​            return 2;
​        }
​        for(int i = 3; i <= target; i ++){z = y;y = x + y;x = z;
​        }
​        return y;