java基础-2

29.java中的4中引用

强引用、软引用、弱引用、虚引用,不同类型的引用体现在GC上。

  • 强引用:如果一个对象具有强引用,它就不会被垃圾回收器回收。即使当前内存空间不足,jvm也不会回收它,而是抛出OutOfMemoryError错误,使程序异常终止。如果想中断强引用和某个对象之间的关联,可以显示的将引用赋值为null,这样一来的话,jvm在合适的时间就会回收该对象。
  • 软引用:在使用软引用时,如果内存空间足够,软引用就能继续被使用,而不会被垃圾回收器回收,只有在内存不足时,软引用才会被垃圾回收器回收。
  • 弱引用:具有弱引用的对象拥有的声明周期更短。因为当jvm进行垃圾回收,一旦发现弱引用对象,无论当前内存是否充足,都会将弱引用回收。不过由于垃圾回收器是一个优先级较低的线程,所以并不一定能发现弱引用对象。
  • 虚引用:顾名思义就是形同虚设,如果一个对象仅持有虚引用,那么它相当于没有引用,在任何时候都可能被垃圾回收器回收。

30.深拷贝和浅拷贝的区别是什么

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

深拷贝:被复制对象的所有变量都含有与原来的对象相同的值,而那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深拷贝把要复制的对象所引用的对象都复制了一遍。

31.一个java文件内部是否可以有其他类(非内部类)

只能有一个public公共类,但是可以有多个default修饰的类。

32.如何正确退出多层嵌套循环

  • 使用标号和break;
  • 通过在外层循环中添加标识符;

33.内部类的作用

内部类可以有多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独立。在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或者继承同一个类。内部类对象的时刻不依赖于外部类对象的创建。内部类并没有令人疑惑的“is-a”关系,它就是一个独立的实体。

内部类提供了更好的封装,除了该外围类,其他类都不能访问。

class Circle {
    double radius = 0;
     
    public Circle(double radius) {
        this.radius = radius;
    }
     
    class Draw {     //内部类
        public void drawSahpe() {
            System.out.println("drawshape");
        }
    }
}

34.java中使用什么类型表示价格比较好

如果不是特别关心内存和性能的话,使用BigDecimal,否则使用预定义类型的double类型。

35.如何将byte转为String

可以使用String接收byte[]参数的构造器来进行转换,需要注意的点是使用正确的编码,否则会使用平台默认编码,这个编码可能跟原来的编码相同,也可能不同。

public static String byteArrayToStr(byte[] byteArray){
    if(null == byteArray){
        return null;
    }
    String str=new String(byteArray);
    return str;
}

36.可以将int强转为byte类型吗,会产生什么问题

我们可以做强制转换,但是java中的byte是8位,而int是32位,所以如果强制转换为int类型,就会将高24位被丢弃。

37.你知道哪些垃圾回收算法

垃圾回收从理论上非常容易理解,具体的方法有以下几种:

  • 标记-清除算法
  • 标记-复制算法
  • 标记-整理算法
  • 分代回收

38.如何判断一个对象是否应该被回收

这就是所谓的对象存活性判断,常用的方法有两种:

  • 引用计数法
  • 对象可达性分析

由于引用计数法存在相互引用导致无法进行GC的问题,所以目前jvm虚拟机多使用可达性分析算法。

39.简单解释一下垃圾回收

java垃圾huishou机制最基本的做法是分代回收。内存中的区别被划分成不同的世代,对象根据其存活的时间被保存在不同的世代中。一般分为代,年轻、年老和永久代。内存分配是发生在年轻代中。当一个对象存活的时间足够长的时候,它就会被复制到年老代中。对于不同的世代可以使用不同的垃圾回收算法。进行世代划分的出发点是对应用中对象存活的时间进行研究之后得出的统计规律。一般来说,一个应用中大部分对象存活的时间都很短。比如局部变量存活的时间就只在方法的执行过程中。基于这一点,对于年轻代的垃圾回收算法就可以很有针对性。

40.调用System.gc()会发生什么

通知GC开始工作,但是GC真正开始的时间不确定。

41.进程和线程的区别

简而言之,进程是程序运行和资源分配的基本单位,一个程序至少有一个进程,一个进程至少有一个线程。进程在执行过程中拥有独立的内存单元,而多个线程共享内存资源,减少切换次数,从而效率更高。线程是进程的一个实体,是cpu调度和分派的基本单位,是比程序更小的能独立运行的基本单位。同一进程中的多个线程之间可以并发执行。

42.多线程上下文切换

多线程上下文切换是指CPU控制权由一个正在运行的线程切换到另一个就绪并等待获取CPU执行权的线程的过程。

43.创建线程的方式,它们之间的区别

通过实现Runnable接口,或者继承Thread类。相比扩展性,Runnable更优。

java不支持多继承,因此扩展Thread类就代表这个子类不能扩展其他类。而实现Runable接口的类还能扩展另一个类。

类可能只要求可执行即可,继承Thread类的开销过大。

44.Thread类中的start()和run()方法区别

start()方法被用来启动新创建的线程,而start()内部调用了run()方法,这和直接调用run()方法的效果不一样。当你调用run方法的时候,只会在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程。

45.导致线程阻塞的原因有哪些

阻塞指的是暂停一个线程执行以等待某个条件发生(如某资源就绪)

方法说明
sleep()sleep()允许指定以毫秒为单位的一段时间作为参数,它使得线程在指定时间内进入阻塞状态,不能得到CPU时间,指定的时间一过,线程重新进入可执行状态。典型的,sleep()被用在等待某个资源就绪的情形;测试发现条件不满足后,让线程阻塞一段时间后重新测试,直到条件满足为止
suspend()和resume()两个方法配套使用,suspend()使用线程进入阻塞状态,并且不会自动恢复,必须其对应的resume()被调用,才能使得线程重新进入可执行状态。典型的,suspend()和resume()被用在等待另一个线程产生的结果的情形;测试发现结果还没有产生后,让线程阻塞,另一个线程产生结果后,调用resume()使其恢复。
yield()yield()使当前线程放弃当前已经获得的CPU执行时间,但不使当前线程阻塞,即线程仍然处于可执行状态,随时可能再次分得CPU时间。
wait()和notify()两个方法配套使用,wait()使得线程处于阻塞状态,它有两种形式,一种允许指定以毫秒为单位的一段时间作为参数,另一种没有参数,前者当对应的notify()被调用或者超出指定时间时,线程重新进入可执行状态,后者则必须对应notify()被调用。

46.sleep()和yield()区别

sleep()方法和yield()方法是Thread的静态方法,都会使当前运行的线程放弃CPU,把运行机会让给别的线程。

两者的区别在于:

  • sleep()方法会给其他线程运行的机会,而不考虑其他线程的优先级,因此会给较低优先级线程一个运行的机会;yield()方法只会给同优先级或者更高优先级线程一个运行的机会。
  • 当线程执行了sleep(long millis)方法之后进入到阻塞状态,参数millis指定睡眠时间;当线程执行了yield()方法之后就转到就绪状态;
  • sleep()方法声明抛出InterruptException异常,而yield()方法没有声明抛出任何异常;
  • sleep()方法比yield()方法具有更好的移植性。

47.产生死锁的条件

1)互斥条件:一个资源每次只能被一个进程使用;

2)请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不变;

3)不剥夺条件:进程已获得的资源,在未使用完之前,不能强制剥夺;

4)循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

48.为什么wait()方法和notify()/notifyAll()方法要在同步块中被调用

这是JDK强制规定的,wait()方法和notify()/notifyAll()方法在调用前都必须获得对象的锁。

49.wait()方法和notify()/notifyAll()方法在放弃对象监视器时有什么区别

wait()方法和notify()/notifyAll()方法在放弃对象监视器的时候区别在于:wait()方法立即释放监视器,notify()/notifyAll()方法则会等待线程剩余代码执行完毕才会放弃对象监视器。

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

1)sleep()来自Thread类,和wait()来自object类。调用sleep()方法的过程中,线程不会释放对象锁,而调用wait()方法会释放锁。

2)sleep()睡眠后不出让系统资源,wait()让其他线程可以占用CPU

3)sleep(milliseconds)需要指定一个睡眠时间,时间一到会自动唤醒,而wait()需要配合notify()/notifyAll()使用。

51.为什么wait、notify和notifyAll这些方法不在Thread类当中

一个很明显的原因是java提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程获得。如果线程需要等待某些锁,那么调用对象的wait方法就有意义了。如果wait方法定义在Thread类中,线程正在等待的是哪个锁就不明显了。简单的说,由于wait、notify和notifyAll都是锁级别的操作,所以把它们定义在object类中,因为锁属于对象。

52.怎样唤醒一个阻塞的线程

如果线程是因为调用了sleep()、wait()、join()方法导致阻塞,可以中断线程,并且抛出InterruptedException来唤醒它;如果线程遇到了IO阻塞,无能为力,因为IO是操作系统实现的,java代码并没有办法直接接触到操作系统。

53.synchronized与Reentrantlock的区别

synchronized是和if、else、for、while一样的关键字,Reentrantlock是类,这是两者的本质区别。既然Reentrantlock是类,那么它就提供了比synchronized更多更灵活的特性,可以被继承、可以有方法、可以有各种各样的类变量。Reentrantlock比synchronized的扩展性体现在几点上:

1)ReentrantLock可以对获取锁的等待时间进行设置,这样就避免了死锁 

2)ReentrantLock可以获取各种锁的信息 

3)ReentrantLock可以灵活地实现多路通知 

另外,二者的锁机制其实也是不一样的:ReentrantLock底层调用的是Unsafe的park方法加锁,synchronized操作的应该是对象头中mark word。

54.FutureTask是什么

这个其实前面有提到过,FutureTask表示一个异步运算的任务。FutureTask里面可以传入一个Callable的具体实现类,可以对这个异步运算的任务的结果进行等待获取、判断是否已经完成、取消任务等操作。当然,由于FutureTask也是Runnable接口的实现类,所以FutureTask也可以放入线程池中。

55.一个线程如果出现了运行时异常怎么办

如果这个异常没有被捕获的话,这个线程就停止执行了。另外重要的一点是:如果这个线程持有某个对象的监视器,那么这个对象的监视器会被立即释放。

56.如何在两个线程间共享数据

通过在线程间共享对象就可以了,然后通过wait/notify/notifyAll,await/signal/signalAll进行唤起和等待,比方说阻塞队列BlockingQueue就是为线程之间共享数据而设计的。

57.为什么要使用线程池

避免频繁的创建和销毁线程,达到线程对象的重用。另外使用线程池,还可以根据项目灵活的控制并发的数目。

58.java中使用的线程调度算法是什么

抢占式,一个线程用完CPU之后,操作系统会根据线程优先级、线程饥饿情况等数据算出一个总的优先级并分配下一个时间片给某个线程执行。

59.Thread.sleep(0)的作用是什么

由于java采用抢占式的线程调度算法,因此可能会出现某条线程常常获取到CPU控制权的情况,为了让某些优先级比较低的线程能获得CPU控制权,可以使用Thread.sleep(0) 手动触发一次操作系统分配时间片的操作,这也是平衡CPU控制权的一种操作。

60.什么是乐观锁和悲观锁

乐观锁:认为竞争不总是会发生,因此它不需要持有锁,将比较——替换这两个动作作为一个原子操作尝试去修改内存中的变量,如果失败则表示发生冲突,那么久应该有相应的重试逻辑。

悲观锁:悲观锁认为竞争总是会发生,因此每次对某资源进行操作是时,都会持有一个独占锁,就像synchronized,不管三七二十一,直接上了锁就操作资源了。

61.java中的++操作符线程安全吗

不是线程安全的操作,它涉及到多个指令,如读取变量值,增加,然后存储回内存,这个过程可能会出现多个线程交叉。

62.poll()和remove()方法的区别

poll()和remove()都是从队列中取出一个元素,但是poll()在读取元素失败时会返回空,但是remove()失败时会抛出异常。

63.LinkedHashMap和PriorityQueue的区别

priorityQueue是一个优先级队列,保证最高或者最低优先级的元素总是在队列头部,但是LinkedHashMap维持的顺序是元素插入的顺序。当遍历一个PriorityQueue时,没有任何顺序保证,但是linkedHashMap输出顺序是元素插入的顺序。

64.ArrayList和LinkedList的区别

最明显的区别是ArrayList底层的数据结构是数组,支持随机访问,而LinkedList底层的数据结构是双向循环链表,不支持随机访问。使用下标访问一个元素,ArrayList的时间复杂度是O(1),而LinkedList是O(n).

65.comparator和comparable的区别

comparable接口用于定义对象的自然顺序,而comparator通常用于定义用户定制的顺序。comparable总是只有一个,但是可以有多个comparator来定义对象顺序。

66.如何实现集合排序

你可以使用有序集合,如TreeSet或TreeMap,你也可以使用有顺序的集合,如list,然后通过collections.sort()来排序。

 

 

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CSDN砖家

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值