黑马程序员-----曾经遇到的几题面试题详解

------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

在我大四的时候,自己去面试过几家软件公司,虽然都是以失败告终,但是我知道失败乃成功之母。

每一次的面试题我都会记录下来,然后认真研究,环绕着题目核心去做例子。
今天在看视频学习的过程中,我遇到了和面试时候一样的一道题,我却做不出来,当时的我很郁闷,慢慢地我学会总结,
希望通过总结来加深我自己的记忆。

面试题一:解释Runnable接口与Thread类的区别
解释Runnable接口与Thread类的区别。大家都知道,Runnable接口与Thread类都是用来创建线程类的,它们都需要实现run()方法,似乎没什么区别。
其实,它们还是有区别的,请看以下。
首先大家都应该知道,JAVA的类不允许多继承的,也就是只能继承自一个类。
那么,如果线程类继承了Thread以后,它就不能再继承其他的类了。
而Runnable接口,就不会有这样的问题,因为类是可以实现多个接口的。
另外,Thread提供了很多关于线程的方法,例如,获取线程id线程名线程状态等方法。
对于比较复杂一点的线程,可能就需要run()方法中调用这些方法,而Runnable接口使用起来就没有那么方便了。

如果让一个线程类实现Runnable接口,那么当调用这个线程的对象开辟多个线程时,
可以让这些线程调用同一个变量;若这个线程是由继承Thread类而来,那么就会麻烦一些,
则要通过内部类来实现上述功能,利用的就是内部类可以任意访问外部变量这一特性。
举个例子:

class MyThread implements Runnable {  //实现Runnable接口
      int index = 0;  		//index变量
      public void run() {
            ...............
}
}
class MyThread {
      int index = 0;
private class InnerClass extends Thread {  //定义一个内部类,继承Thread
      public void run() {
            .............
}
}
      Thread getThread() {
      return new InnerClass();  //这个函数的作用是返回InnerClass的一个匿名对象
}
}

总结:
(1)、线程类继承自Thread则不能继承自其他类,而Runnable接口可以。
(2)、线程类继承自Thread相对于Runnable来说,使用线程的方法更方便一些。
(3)、实现Runnable接口的线程类的多个线程,可以更方便的访问同一个变量,而Thread类则需要内部类来进行替代。

面试题二:如何用sychronized来让线程同步
多线程一旦操作同一块内存的数据,就可能造成数据的混乱,也就是常说的线程安全。
Java针对这样的问题,采用sychronized关键字来帮助开发者解决这样的问题。那么sychrnized是怎么使用的呢,下面我来讲解一下。

我先给出一个出现线程安全问题的代码:
Class MyThread extends Thread {
    public static int index;  //静态变量index
    public void run() {
        for(int i = 0;i<100;i++) {
        //循环打印index加1以后的值
       System.out.println(getName()+":"+index++);
} 
}
}
public class SyncTest {
    public static void main(String[] args) {
        new MyThread().start();
        new MyThread().start();
        new MyThread().start();
}
}

通过以上的运行结果可以看出,线程之间并没有等谁执行完以后再执行,而是交织着执行的,这不符合程序的本意
这就出现了一个好办法,就是让它们可能会出现混乱的代码同步,也就是线程之间依次排队获取资源。
在Java中,使用sychronized关键字来保证线程的同步。sychronized的工作原理是这样的:
每个对象都可以有一个线程锁,sychronized可以用任何一个对象的线程锁来锁住一段代码,
任何想进入该段代码的线程必须在解锁以后才能继续执行,否则就进入等待状态
其中,只有等占用该锁资源的线程执行完毕以后,该锁资源才会被释放。
例如,以上程序代码中的MyThread类可以这样来写,就可以很好地解决冲突的问题:

class MyThread extends Thread {
    public static int index;
    public static Object obj = new Object();
    //用任意一个对象来加锁
    public void run() {
    	synchronized(obj) {
        	for(int i = 0;i<100;i++) {
         	System.out.println(getName()+":"+index++);
			} 
		}
	}
}

以上代码中,把for循环的代码块加上了obj对象的锁,当一个线程进入该段代码以后,
即使其他线程进入run()方法,也需要在获得锁的情况下,才能继续运行。
另外,sysnchronized还可以加在成员方法上,这就叫做 同步方法 ,此时的锁是加在this所引用得对象上的。
加在方法上面,还可以达到把代码段进行进一步细分的作用,也免去了需要找一个对象来加锁的步骤。

注意:不论是同步代码块,还是同步方法,它们都会损失一些效率。
因此,开发者应该根据线程可能出现问题的地方加上同步,
而不必大段大段的加上同步锁,这样可能是程序的性能降低。

总结:
sychronized关键字代表要为某一段代码加上一个同步锁,这样的锁是绑定在一个对象上边的。
如果是同步代码块,需要为该sychronized关键字提供一个对象的引用;
如果是同步方法,只需要加一个sychronized关键字的修饰。

面试题三:什么是序列化
序列化,又称“串化”,可以形象的把它理解为把JAVA对象内存中的数据采编成一串二进制的数据。
然后把这些数据存放在可以持久的数据存储设备,如磁盘。
当需要还原这些数据的时候,再通过反序列化的过程,把对象又重新还原到内存中。

java.io.Serializable接口是可以进行序列化的类的标志接口,该接口本身没有任何需要实现的抽象方法。
它仅仅是用来告诉JVM该类的对象的可以进行序列化的,并且它的序列化ID由静态的serialVersionUID变量提供

serialVersionUID变量其实是一个静态的long型的常量,它的作用在序列化和反序列化的过程中,起到了辨别一个类的作用。
在反序列化的时候,如果两个类的类名完全相同,
就通过serialVersionUID来判断该类是否符合要求,如果不行,则抛出异常。

Java的I/O提供了一对类用作对象的序列化和反序列化,主要包括ObjectOutputStream和ObjectInputStream。
它们的用法和字节流相似,只不过此时处理的是对象,而不仅仅是字节数据了。

总结:
序列化本质上就是把对象内存中的数据按照一定的规则,变成一系列的字节数据,然后再把这些字节数据写入到流中
而反序列化的过程相反,先读取字节数据,然后再重新组装成Java对象
所有需要进行序列化的类,都必须实现Serializable接口,必要时还需要提供静态的常量serialVersionUID。
  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值