Java面试简答题(二)


1. StringBuffer和StringBuilder和String的区别

相同点:

  1. 底层都是用数组实现的

  2. 都是CharSequence的子类

  3. 都是用final修饰的,不能被继承

 不同点:

  1. String创建的对象是不可变的,改变String对象的值实际是指向新的对象

  2. StringBuffer和StringBuilder创建的对象是可变的。

  3. StringBuffer是线程安全的,StringBuilder是非线程安全的。

2.int和Integer的区别

相同点: 1.都可以定义变量不同点: 1. int 是基本数据类型,Integer是引用类型,是int的包装类. 2. int 和 Integer 可以相互转化  int -> Integer      Integer.valueOf();  Integer -> int       int.intValue();  

3. Integer对象的值在-128~127范围内时会引用缓存 4. int的默认值为0, Integer的默认值为Null 5. Integer可以区分出未赋值和值为0的区别,int 则无法表达出未赋值的情况

3.值传递和引用传递的区别

   1.在值传递过程中,形式参数类型是基本数据类型,当用方法调用时,实际参数将它的值传递给相应的形式参 数,而形式参数只是用实际参数的值初始化自己的存储单元内容,是两个不同的存储单元,因此在方法执 行中形式参数值的改变不影响实际参数的值。    2.在引用传递中,形式参数类型是引用数据类型参数,或者叫做“传地址”。当方法调用时,实际参数对象(或 数组)与形式参数指向同一个地址,执行方法的过程中,对形式参数的操作实际上就是对实际参数的操 作,在方法结束后这一结果被保留下来,于是形式参数的改变将会影响实际参数的值。 注意 1.按值传递:指的是在方法调用时,传递的参数是按值的拷贝传递。传递的是值的拷贝,也就是说传递 后就互不相关了. 2.引用传递:指的是在方法调用时,传递的是引用的地址,也就是变量所对应的内存空间的地址. 也就是说 传递前和传递后都指向同一个引用(也就是同一个内存空间)

4.对象在内存中的状态

  1. 可达状态:当一个对象被创建后,若有一个以上的变量引用它,则这个对象在程序中处于可达状态。

  2. 可恢复状态:如果程序中某个对象不再有任何变量引用它,对象处于可恢复状态,这种状态下,系统的垃圾回收机制准备回收该对象占用的内存,在回收该对象之前,系统会调用所有可恢复对象的finalize()方法进行资源清理。如果在调用finalize()方法时重新让一个变量引用该对象,则这个对象会再次变为可达状态,否则进入不可达状态

  3. 不可达状态:当对象与所有变量的关系被切断,且系统已经调用所有对象的finalize()方法后依然没有使该对象变成可达状态,那么这个对象将永久性的失去引用,最后变成不可达状态。只有一个对象处于不可达状态时,系统才会真正回收该对象所占有的资源。

5.四种引用

  1. 强引用:对象被变量引用,不会被垃圾回收器回收

  2. 软引用:垃圾回收器启动后,当内存不足时,会回收软引用对象

  3. 弱引用:垃圾回收器启动后,不管内存是否足够,都会回收

  4. 虚引用:等价于没有引用

6.对象序列化

  • 序列化就是将对象的状态(对象的属性)存储到特定的存储介质中的过程, 也就是将对象状态转换为可保持或可传输格式的过程.

    • 序列化的核心:1.保存对象的状态

      2.对象状态可储存

  • 反序列化则是从特定的存储介质中读取数据并重新构建成对象的过程

    注意:Java中只有实现了java.io.Serializable接口的类的对象才能被序列化

7.克隆(clone)

  • 1.浅复制(浅克隆)

(要实现浅克隆被克隆的类必须实现Cloneable接口)

被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。

继承自java.lang.Object类的clone()方法是浅复制

  • 2.深复制(深克隆)

(要实现深克隆被克隆类以及被克隆类的引用必须实现Serializable接口)

被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。深克隆的实现实际上是对对象的序列化和反序列化.

8.请简述调用start()方法和run()方法的区别

  1. run()方法中定义线程执行的任务,start()方法则是用来启动线程

  2. 直接调用run方法jvm会将run()方法当做普通实例方法执行,而不是启动该线程

  3. 调用start()方法后,该线程进入就绪状态,当线程调度器为该线程分配时间片以后,该线程开始执行run方法

9.请简述创建线程的两种方式,及区别

  1. 创建线程可以继承Thread类,并重写run()方法

  2. 可以继承Runnable接口,并实现/重写run()方法

  3. 因为java中是单继承,通过继承Thread类创建线程后,该类则不能继承其他类,继承Runnable接口则可以继承其他类或者其他接口,有利于类的扩展。

  4. 继承Thread类可以直接调用start()方法启动线程,继承Runnable接口后则必须通过Thread的构造方法,传入改对象创建Thread对象再调用star()方法启动线程。

10.请简述sleep()方法和wait()的区别

相同点:

  1. sleep和wait方法都可以使线程进入阻塞状态

  2. sleep和wait方法都会抛出InterruptedException

不同点:

  1. sleep()方法在休眠结束后重新进入就绪状态,wait()方法会一直阻塞线程直到被唤醒

  2. sleep方法是Thread类的成员方法,wait()方法是Object类的成员方法

11.线程生命周期

  1. 新生:线程类new出对象,此时内存中仅仅是为对象分配内存空间,为成员变量赋初始值。

  2. 就绪:调用线程start()方法后线程进入就绪状态,此时线程并不是立即执行,而是等待线程调度器分配时间片,当该线程得到资源后,调用线程run()方法

  3. 运行:线程run()方法运行时。

  4. 阻塞:当线程因为某些原因不能继续运行,并放弃所占用的处理器资源时,就进入阻塞状态,阻塞状态不能直接进入运行状态,而是当阻塞结束是,重新进入就绪状态,重新等待线程调度器分配资源。当出现如下情况时,线程进入阻塞。

    1. 线程调用sleep()方法时,当sleep()休眠时间结束时,线程进入就绪状态

    2. 线程调用阻塞式IO方法,在该方法返回之前,线程被阻塞

    3. 线程试图获得一个同步监视器,但是该监视器正在被其他线程持有

    4. 线程在等待某个notify()

  5. 死亡:run()方法执行完成,或者线程抛出异常

12.实现线程同步的方法

1.synchronized

非线程安全:多个线程访问同一对象实例变量时,出现读取结果不一致(或者称为脏度),就是非线程安全

  1. 同步方法

  2. 同步代码块

    1. 必须保证锁唯一

2 .同步锁

3.volatile


你可能感兴趣的:(Java面试简答题(二))