面试准备:Java常见面试题汇总(二)

2023-11-07

面试准备:Java常见面试题汇总(一)

面试准备:Java常见面试题汇总(二)

面试准备:Java常见面试题汇总(三)

文章目录

43.java 中的 Math.round(-1.5) 等于多少?*

Math.round采用了四舍五入,四舍五入的原理是在参数上加0.5然后做向下取整。
Math.round(1.5)的返回值是2,Math.round(-1.5)的返回值是-1。

44.String str="abc"与 String str=new String(“abc”)一样吗?

不一样。

  • String str="abc"的方式:
    java虚拟机会在字符串常量池里查找是否存在"abc"的驻留字符串,如果不存在,则在字符串常量池中创建"abc"这个驻留字符串(可以理解为引用),(JDK1.7在堆区为字符串“abc”开辟内存,JDK1.7之前是直接在字符串常量池开辟“abc”的内存),然后将池中"abc"这个对象的引用地址作为返回。

  • String str=new String(“abc”)的方式:
    会在堆上创建一个String对象,这个对象包含“abc“的final字符数组。

String s="计算机";
String s1 = new String("计算机");//s!=s1, s.equals(s1)
String s2 = s1.intern();//s==s2

String str1 = "str";
String str2 = "ing";
String str3 = "str" + "ing";//字符串常量池中查找驻留字符串
String str4 = str1 + str2; //在堆上创建的新的对象

45.如何将字符串反转?*

1.利用 StringBuffer 或 StringBuilder 的 reverse 成员方法:
2.利用 String 的 toCharArray 方法先将字符串转化为 char 类型数组,然后将各个字符进行重新拼接:

46.String 类的常用方法都有那些?*

toCharArray()
substring()
charAt()
length()
split()
trim()
toUpperCase()

47.抽象类必须要有抽象方法吗?*

抽象类可以没有抽象方法,但是如果你的一个类已经声明成了抽象类,即使这个类中没有抽象方法,它也不能再实例化,即不能直接构造一个该类的对象。如果一个类中有了一个抽象方法,那么这个类必须声明为抽象类,否则编译通不过。

48.普通类和抽象类有哪些区别?*

抽象类用abstract修饰,且不能被实例化。
抽象类不能是final的。
抽象方法不可以使用private、final或者static。
抽象类的子类必须实现抽象类中所有抽象方法,否则这个子类也是抽象类。

49.java 中 IO 流分为几种?Reader和InputStream区别在哪?BufferInputStream原理?

  • 按照流的流向分,可以分为输入流和输出流;
  • 按照操作单元划分,可以划分为字节流和字符流;

字节流和字符流又有什么区别呢?

  1. 字节流也称为原始数据,需要用户读入后进行相应的编码转换。而字符流的实现是基于自动转换的,读取数据时会把数据按照JVM的默认编码自动转换成字符。
  2. 字节流可用于任何类型的对象,包括二进制对象,而字符流只能处理字符或者字符串

关于用没用到缓冲区,上层比如bufferdInputstream和bufferdReader肯定都是用到了的,但是底层Inputstream没有用到,而Reader用到了。


字节输入流和输出流:InputStream和OutputStream
字符输入流和输出流:Reader和Writer
在这里插入图片描述


参考:Java BufferedInputStream原理及设计模式分析

50.BIO、NIO、AIO 有什么区别?

参考:Java架构直通车——BIO、NIO、AIO

51.Files的常用方法都有哪些?*

Files.exists() 检测文件路径是否存在
Files.createFile()创建文件
Files.createDirectory()创建文件夹
Files.delete() 删除文件或者目录
Files.copy() 复制文件
Files.move() 移动文件
Files.size()查看文件个数
Files.read() 读取文件
Files.write()写入文件

52.Collection 和 Collections 有什么区别?

java.util.Collection 是一个集合接口。它提供了对集合对象进行基本操作的通用接口方法,比如add()、remove()、size()等等。

java.util.Collections 是一个包装类。它包含有各种有关集合操作的静态多态方法,比如sort()、reverse()、copy()、fill()。

53.说一下HashMap和HashSet的实现原理?

HashMap 的实现原理:
HashMap是基于Hash算法实现的,
我们通过put(key,value)存储数据,通过get(key)来获取数据

当传入key时,HashMap会根据Key.hashCode()计算出Hash值,根据Hash值将value保存在bucket里 ,。

当计算出相同的Hash值时,我们称之为Hash冲突,HashMap 的做法是用链表和红黑树存储相同Hash值的value,
当hash冲突的个数比较少时,使用链表存储,
否则使用红黑树。

HashSet 的实现原理:
HashSet是基于HashMap实现的,HashSet 底层使用HashMap来保存所有元素,
因此HashSet 的实现比较简单,相关HashSet 的操作,基本上都是直接调用底层HashMap的相关方法来完成,HashSet不允许有重复的值(通过Hashcode()和equals()方法来去重),并且元素是无序的。

54.如何实现数组怎么转换成ArrayList?*

//方法1
List list = new ArrayList<>(Arrays.asList("a", "b", "c"))
//方法2
Integer [] myArray = { 1, 2, 3 };
List myList = Arrays.stream(myArray).collect(Collectors.toList());
//基本类型也可以实现转换(依赖boxed的装箱操作)
int [] myArray2 = { 1, 2, 3 };
List myList = Arrays.stream(myArray2).boxed().collect(Collectors.toList());

55.在 Queue 中 poll()和 remove()有什么区别?*

poll()和remove()都将移除并且返回对头,但是在poll()在队列为空时返回null,而remove()会抛出NoSuchElementException异常。

56.ArrayList 和 Vector 的区别是什么?*

ArrayList 和Vector底层是采用数组方式存储数据

Vector:
线程同步(因为效率较低,现在已经不太建议使用)
当Vector中的元素超过它的初始大小时,Vector会将它的容量翻倍,
ArrayList:
线程不同步,但性能很好
当ArrayList中的元素超过它的初始大小时,ArrayList只增加50%的大小

57.哪些集合类是线程安全的?

常见的并发集合:
ConcurrentHashMap:线程安全的HashMap的实现
CopyOnWriteArrayList:线程安全且在读操作时无锁的ArrayList
CopyOnWriteArraySet:基于CopyOnWriteArrayList,不添加重复元素
ArrayBlockingQueue:基于数组、先进先出、线程安全,可实现指定时间的阻塞读写,并且容量可以限制
LinkedBlockingQueue:基于链表实现,读写各用一把锁,在高并发读写操作都多的情况下,性能优于ArrayBlockingQueue
ConcurrentLinkedQueue:基于链接节点的无界线程安全队列。

另外还有
Vector:就比Arraylist多了个同步化机制(线程安全)。
Hashtable:就比Hashmap多了个线程安全。
Stack:继承自Vector

58.迭代器 Iterator 是什么?

迭代器(Iterator) 迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象。参考:Java设计模式——迭代器模式
以ArrayList为例,它将Iterator作为ArrayList的一个私有内部类,内部类可以访问外部ArrayList的数组,然后通过一个游标cursor来遍历整个数组的。

迭代器可以使用remove()删除对象(因为使用的是游标),而直接使用foreach是不行的。而如果使用for循环for(int i=0;i<list.size();i++),如果在中途删除了这个对象,会导致list.size()改变。
而在使用迭代器remove的时候,会调用ArrayList的remove方法,并且正确修改游标cursor的位置。在此期间不允许list进行list.add()或者list.remove()操作,因为这回改变expectedModCountmodCount这两个值,导致迭代器抛出ModificationException的错误。

59.怎么确保一个集合不能被修改?*

采用Collections包下的unmodifiableMap方法,通过这个方法返回的map,是不可以修改的。他会报 java.lang.UnsupportedOperationException错。

同理:Collections包也提供了对list和set集合的方法。
Collections.unmodifiableList(List)
Collections.unmodifiableSet(Set)

unmodifiableMap会对Map进行包装,返回一个不可修改的Map。

60.并发和并行有什么区别?*

并发是指一个处理器同时处理多个任务。并行是指多个处理器或者是多核的处理器同时处理多个不同的任务。并发是逻辑上的同时发生(simultaneous),而并行是物理上的同时发生。

61.线程和进程之间的区别?

1、进程是一段正在执行的程序,是资源分配的基本单元,而线程是CPU调度的基本单元。
2、进程间相互独立,进程之间不能共享资源。一个进程至少有一个线程,同一进程的各线程共享整个进程的资源(寄存器、堆栈、上下文)。

线程间可以通过直接读写同一进程中的数据进行通信,但是进程通信需要借助 IPC。

3、线程的创建和切换开销比进程小,换句话说线程是轻量级的进程。

由于创建或撤销进程时,系统都要为之分配或回收资源,如内存空间、I/O 设备等,所付出的开销远大于创建或撤销线程时的开销。类似地,在进行进程切换时,涉及当前执行进程 CPU 环境的保存及新调度进程 CPU 环境的设置,而线程切换时只需保存和设置少量寄存器内容,开销很小。

62.Java线程之间怎么通信?

两种通信机制:共享内存机制和消息通信机制。

  1. 共享内存机制

使用同步方法:
syncrhoized加锁的线程的Object类的wait()/notify()/notifyAll()
ReentrantLock类加锁的线程的Condition类的await()/signal()/signalAll()
CountDownLatch
CyclicBarrier

或者使用:轮询+volatile关键字

public class TestSync {
    // 定义一个共享变量来实现通信,它需要是volatile修饰,否则线程不能及时感知
    static volatile boolean notice = false;

    public static void main(String[] args) {
        List<String>  list = new ArrayList<>();
        // 实现线程A
        Thread threadA = new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                list.add("abc");
                System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:" + list.size());
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (list.size() == 5)
                    notice = true;
            }
        });
        // 实现线程B
        Thread threadB = new Thread(() -> {
            while (true) {
                if (notice) {
                    System.out.println("线程B收到通知,开始执行自己的业务...");
                    break;
                }
            }
        });
        // 需要先启动线程B
        threadB.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 再启动线程A
        threadA.start();
    }
}
  1. 消息通信机制

使用java.io.PipedInputStream 和 java.io.PipedOutputStream进行通信。
一个线程发送数据到输出管道,另一个线程从输入管道读数据。

package pipeInputOutput;

import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;

public class Run {

    public static void main(String[] args) {

        try {
            WriteData writeData = new WriteData();
            ReadData readData = new ReadData();

            PipedInputStream inputStream = new PipedInputStream();
            PipedOutputStream outputStream = new PipedOutputStream();

            //将两个Stream之间产生通信链接,这样才能将数据进行输入输出,下面两种方式都可以,其一即可
            //inputStream.connect(outputStream);
            outputStream.connect(inputStream);

            //开启读线程
            ThreadRead threadRead = new ThreadRead(readData, inputStream);
            threadRead.start();

            Thread.sleep(2000);

            //开启写线程
            ThreadWrite threadWrite = new ThreadWrite(writeData, outputStream);
            threadWrite.start();

        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

}

63.守护线程是什么?

JAVA分为守护线程daemon thread和用户线程user thread。
当 JVM 中不存在任何一个正在运行的非守护线程(用户线程)时,则 JVM 进程即会退出。
换句话说,守护线程拥有自动结束自己生命周期的特性,而非守护线程不具备这个特点。

通常来说,守护线程经常被用来执行一些后台任务,但是呢,你又希望在程序退出时,或者说 JVM 退出时,线程能够自动关闭,此时,守护线程是你的首选。

JVM 中的垃圾回收线程就是典型的守护线程,如果说不具备该特性,会发生什么呢?
当 JVM 要退出时,由于垃圾回收线程还在运行着,导致程序无法退出。

64.创建线程有哪几种方式?

  1. 继承Thread类,并重写run()方法。
class Runner extends Thread{
    @Override
    public void run() {
        super.run();
        System.out.println("ok");
    }

    public static void main(String[] args) {
        new Runner().start();
    }
}
  1. 继承Runnable接口,并重写run()方法
class Runner implements Runnable{
    @Override
    public void run() {
        System.out.println("ok");
    }
    public static void main(String[] args) {
        new Thread(new Runner()).start();
    }
}

实际上Thread也是继承了run方法的。

  1. 通过Callable和Future创建线程。
class Runner implements Callable<String> {
    @Override
    public String call() throws Exception {
        System.out.println("运行ok");
        return "success";
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Runner runner=new Runner();
        FutureTask<String> futureTask=new FutureTask<>(runner);
        new Thread(futureTask).start();
        System.out.println("执行后返回值:"+futureTask.get());
    }
}
  1. 通过线程池
class Runner{
    public static void main(String[] args) {
        ExecutorService pool=Executors.newFixedThreadPool(2);
        for (int i=0;i<5;i++){
            pool.submit(()->{//可以使用Callable或者Runnable
                System.out.println("ok");
            });
        }
    }
}

65.说一下 runnable 和 callable 有什么区别?

1、两者最大的不同点是:实现Callable接口的任务线程能返回执行结果;而实现Runnable接口的任务线程不能返回结果;
2、Callable接口的call()方法允许抛出异常;而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛;

Callable接口支持返回执行结果,此时需要调用FutureTask.get()方法实现,此方法会阻塞主线程直到获取‘将来’结果;当不调用此方法时,主线程不会阻塞!

66.线程有哪些状态?

线程状态分别是:新建、就绪、运行、阻塞、死亡。
在这里插入图片描述

67.sleep() 和 wait() 有什么区别?

  1. 同步锁的对待不同:
    sleep()后,程序并不会不释放同步锁。
    wait()后,程序会释放同步锁。

  2. 用法的不同:
    sleep()可以用时间指定来使他自动醒过来。如果时间不到你只能调用interreput()来强行打断。
    wait()可以设定时间,也可以用notify()直接唤起。

  3. 属于不同的类:
    sleep()的类是Thread。
    wait()的类是Object。

68.notify()和 notifyAll()有什么区别?

notifyAll调用后,会将全部线程由等待池移到锁池,然后参与锁的竞争,竞争成功则继续执行,如果不成功则留在锁池等待锁被释放后再次参与竞争。而notify只会随机唤醒一个线程。

锁池:假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized方法(或者synchronized块),由于这些线程在进入对象的synchronized方法之前必须先获得该对象的锁的拥有权,但是该对象的锁目前正被线程A拥有,所以这些线程就进入了该对象的锁池中。
等待池:假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁后,进入到了该对象的等待池中

69.线程的 run()和 start()有什么区别?

调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。

70.创建线程池有哪几种方式?

Executors目前提供了5种不同的线程池创建配置:

1、newCachedThreadPool(),它是用来处理大量短时间工作任务的线程池,具有几个鲜明特点:它会试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线程;如果线程闲置时间超过60秒,则被终止并移除缓存;长时间闲置时,这种线程池,不会消耗什么资源。其内部使用SynchronousQueue作为工作队列,这个阻塞队列没有存储空间,这意味着只要有请求到来,就必须要找到一条工作线程处理他,如果当前没有空闲的线程,那么就会再创建一条新的线程

2、newFixedThreadPool(int nThreads),重用指定数目(nThreads)的线程,其背后使用的是无界的工作队列,任何时候最多有nThreads个工作线程是活动的。这意味着,如果任务数量超过了活动线程数目,将在工作队列中等待空闲线程出现;如果工作线程退出,将会有新的工作线程被创建,以补足指定数目nThreads

3、newSingleThreadExecutor(),它的特点在于工作线程数目限制为1,操作一个无界的工作队列,所以它保证了所有的任务都是被顺序执行,最多会有一个任务处于活动状态,并且不予许使用者改动线程池实例,因此可以避免改变线程数目

4、newSingleThreadScheduledExecutor()和newScheduledThreadPool(int corePoolSize),创建的是个ScheduledExecutorService,可以进行延时任务或定时任务

5、newWorkStealingPool(int parallelism),这是一个经常被人忽略的线程池,Java 8 才加入这个创建方法,其内部会构建ForkJoinPool,利用Work-Stealing算法,并行地处理任务,不保证处理顺序。Work-Stealing的适用场景是不同的任务的耗时相差比较大,即某些任务需要运行较长时间,而某些任务会很快的运行完成;但是如果任务的耗时很平均,因为窃取任务时不同线程需要抢占锁,这可能会造成额外的时间消耗,而且每个线程维护双端队列也会造成更大的内存消耗。

所谓Work-Stealing,在ForkJoinPool中的实现为:线程池中每个线程都有一个互不影响的任务队列(双端队列),线程每次都从自己的任务队列的队头中取出一个任务来运行;如果某个线程对应的队列已空并且处于空闲状态,而其他线程的队列中还有任务需要处理但是该线程处于工作状态,那么空闲的线程可以从其他线程的队列的队尾取一个任务来帮忙运行——感觉就像是空闲的线程去偷人家的任务来运行一样,所以叫“工作窃取”。

6、使用new ThreadPoolExecutor()自定义。

遇到一个面试题,假如让你来定义ThreadPoolExecutor的参数,应该有什么?

//构造函数参数
public ThreadPoolExecutor(
		int corePoolSize,
        int maximumPoolSize,
        long keepAliveTime,
        TimeUnit unit,
        BlockingQueue<Runnable> workQueue,
        ThreadFactory threadFactory,
        RejectedExecutionHandler handler
)

71.线程池都有哪些状态?

  1. Running: 接受新task, 处理等待的task;
  2. ShutDown: 不接受新task,但处理等待的task;
  3. Stop: 不接受新task, 不处理等待的task, 尝试打断正在执行的task;
  4. Tidying:但所有task都被终止, worCount == 0的时候(workCount是指有效的线程数);
  5. Terminated: 执行完terminated()方法;

  • Running -> ShutDown:
    执行shutdown();

  • Running or ShutDown --> Stop:
    执行shutdownNow();

  • Stop --> Tidying
    当pool为空时

  • ShutDown --> Tidying
    当queue 和 pool都为空时

  • Tidying --> Terminated
    当terminated()方法结束时;

72.线程池中 submit()和 execute()方法有什么区别?

public interface ExecutorService extends Executor {...}

可以看出ExecutorService接口继承自Executor,而这个Executor接口需要实现execute()方法。

public interface Executor {
    void execute(Runnable command);
}

execute()方法的入参为一个Runnable,返回值为void。

而我们再来看submmit()方法是怎么来的:

public interface ExecutorService extends Executor {
  ...
  <T> Future<T> submit(Callable<T> task);

  <T> Future<T> submit(Runnable task, T result);

  Future<?> submit(Runnable task);
  ...
}

它是ExecutorService的实现类实现的,入参可以为Callable<T>,也可以为Runnable,而且方法有返回值Future<T>

总结,从上面的源码以及讲解可以总结execute()和submit()方法的区别:

  1. 接收的参数不一样;
  2. submit()有返回值Future,而execute()没有;
  3. submit()可以进行Exception处理
    可以通过对Future.get()进行抛出异常的捕获,然后对其进行处理。

73.Java是如何保证多线程运行安全的?(即,Java并发编程的特性是什么?)

1.原子性:提供互斥访问,同一时刻只能有一个线程对数据进行操作,(atomic,synchronized);

2.可见性:一个线程对主内存的修改可以及时地被其他线程看到,(synchronized,volatile);

3.有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序,该观察结果一般杂乱无序,(happens-before原则)。

导致线程存在安全问题的原因:

  1. CPU缓存机制导致可见性问题
  2. 线程切换导致原子性问题
  3. 编译优化导致的有序性问题

74.多线程锁的升级原理是什么?

锁的级别从低到高:
偏向锁 -> 轻量级锁 -> 重量级锁

锁分级别原因:
没有优化以前,sychronized是重量级锁(悲观锁), JVM 对 sychronized 关键字进行了优化,把锁分为 无锁、偏向锁、轻量级锁、重量级锁 状态。锁可以升级不可以降级,但是偏向锁状态可以被重置为无锁状态。
(注意:无锁不能算在锁升级里面)。

  • 无锁:
    没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功,其他修改失败的线程会不断重试直到修改成功。
    无锁主要采用CAS(还用到了内存屏障(有序性)、可见性和原子性)。

但是偏向锁的撤销需要等到全局安全点(就是当前线程没有正在执行的字节码)。它会首先暂停拥有偏向锁的线程,然后检查持有偏向锁的线程是否活着。如果线程不处于活动状态,直接将对象头设置为无锁状态。

  • 偏向锁:
    偏向锁,指的就是偏向加锁线程(原因在为什么要引入偏向锁中),该线程是不会主动释放偏向锁的,这样可以降低加锁和解锁带来的性能开销,只有当其他线程尝试竞争偏向锁才会被释放或者升级
    偏向锁的释放或者升级,需要判断锁的对象头和栈帧:比较当前线程的threadID和Java对象头中的threadID是否一致。如果不一致,再判断记录里的线程是否存活,如果不存活,则将对象头设置成无锁状态,并撤销偏向锁;如果线程处于存活状态,升级为轻量级锁。

当一个线程访问同步代码块并获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的线程ID,以后该线程再进入和退出同步块时不需要进行CAS操作来加锁和解锁,只需要简单地测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁。如果测试成功,表示线程已经获得了锁。

为什么要引入偏向锁?
因为经过HotSpot的作者大量的研究发现,大多数时候是不存在锁竞争的,常常是一个线程多次获得同一个锁,因此如果每次都要竞争锁会增大很多没有必要付出的代价,为了降低获取锁的代价,才引入的偏向锁。

  • 轻量级锁:
    轻量级锁考虑的是竞争锁对象的线程不多,而且线程持有锁的时间也不长的情景。因为阻塞线程需要CPU从用户态转到内核态,代价较大,如果刚刚阻塞不久这个锁就被释放了,那这个代价就有点得不偿失了,因此这个时候就干脆不阻塞这个线程,让它自旋这等待锁释放。
    当前只有一个等待线程,则该线程将通过自旋进行等待。但是当自旋超过一定的次数时,轻量级锁便会升级为重量级锁;当一个线程已持有锁,另一个线程在自旋,而此时又有第三个线程来访时,轻量级锁也会升级为重量级锁。

  • 重量级锁:
    指当有一个线程获取锁之后,其余所有等待获取该锁的线程都会处于阻塞状态,防止CPU空转。

其他知识点参考:Java架构直通车——为什么线程切换会导致用户态与内核态的切换?

75.什么是死锁?怎么防止死锁?

死锁是指多个线程(或者进程)在执行过程中,由于竞争资源造成的一种阻塞。

死锁发生的原因:
1、资源互斥,即当资源被一个线程使用(占有)时,别的线程不能使用
2、不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
3、请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
4、循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。

防止死锁:

  1. 有序资源分配法
    对它所必须使用的而且属于同一类的所有资源,必须一次申请完;在申请不同类资源时,必须按各类设备的编号依次申请。
  2. 银行家算法
    在避免死锁方法中允许进程动态地申请资源,但系统在进行资源分配之前,应先计算此次分配资源的安全性,若分配不会导致系统进入不安全状态,则分配,否则等待。
  • 资源共享:采取共同读写机制(破坏互斥条件)
  • 可剥夺资源:即当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源(破坏不可剥夺条件)
  • 资源一次性分配:一次性分配所有资源,这样就不会再有请求了(破坏请求条件)
  • 只要有一个资源得不到分配,也不给这个进程分配其他的资源(破坏请保持条件)
  • 资源有序分配法:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反(破坏环路等待条件)
    ————————————————

76.什么是 java 序列化?什么情况下需要序列化?*

序列化:将 Java 对象转换成字节流的过程。
反序列化:将字节流转换成 Java 对象的过程。

当 Java 对象需要在网络上传输 或者 持久化存储到文件中时,就需要对 Java 对象进行序列化处理。

  • 网络传输:我们将系统拆分成多个服务之后,服务之间传输对象,不管是何种类型的数据,都必须要转成二进制流来传输,接受方收到后再转为数据对象。
  • 数据持久化:比如一个电商平台,有数万个用户并发访问的时候会产生数万个session 对象,这个时候内存的压力是很大的。我们可以把session对象序列化到硬盘中,需要时在反序列化,减少内存压力。

序列化的实现:类实现 Serializable 接口,这个接口没有需要实现的方法。实现 Serializable 接口是为了告诉 jvm 这个类的对象可以被序列化。然后使用一个输出流并通过writeObect(Obejct)方法就可以将实现对象写出。

比如:

		OutputStream op = new FileOutputStream("io"+File.separator+"a.txt");
        ObjectOutputStream ops = new ObjectOutputStream(op);
        ops.writeObject(new Person("vae",1));
         
        ops.close();

注意事项:
声明为 static 和 transient 的成员变量,不能被序列化。static 成员变量是描述类级别的属性,transient 表示临时数据

77.为什么要使用克隆?

开发过程中,有时会遇到把现有的一个对象的所有成员属性拷贝给另一个对象的需求。

浅拷贝介绍:
它会创建一个新对象,其成员变量是基本类型,拷贝的就是基本类型的值;如果属性是引用类型,拷贝的就是内存地址 。

深拷贝介绍:
(1) 对于基本数据类型的成员对象,因为基础数据类型是值传递的,所以是直接将属性值赋值给新的对象。基础类型的拷贝,其中一个对象修改该值,不会影响另外一个(和浅拷贝一样)。
(2) 对于引用类型,比如数组或者类对象,深拷贝会新建一个对象空间,然后拷贝里面的内容,所以它们指向了不同的内存空间。改变其中一个,不会对另外一个也产生影响。
(3) 对于有多层对象的,每个对象都需要实现 Cloneable 并重写 clone() 方法,进而实现了对象的串行层层拷贝。
(4) 深拷贝相比于浅拷贝速度较慢并且花销较大。

78.Java实现深拷贝的两种方式?

通过序列化和反序列化深拷贝参考:
原型模式

通过clone接口:

public class Demo implements Cloneable {
 
    private String name;
 
    private String value;
 
    private DemoInternal demoInternal;
 
    /*省略getter和setter方法*/
 
    @Override
    public Demo clone() {
        Demo demo = null;
        try {
            demo = (Demo) super.clone(); //浅复制
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        demo.demoInternal = demoInternal.clone(); //深度复制
        return demo;
    }
}
public class DemoInternal implements Cloneable {
 
    private String internalName;
 
    private String internalValue;
 
    /*省略getter和setter方法*/
 
    @Override
    public DemoInternal clone() {
        DemoInternal demoInternal = null;
        try {
            demoInternal = (DemoInternal) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return demoInternal;
    }

如果包含了数组,可以new一个新的数组来重新开辟空间。

79. Oracle JDK和OpenJDK有什么区别?*

  1. OpenJDK 是一个参考模型并且是完全开源的,而 Oracle JDK 是 OpenJDK 的一个实现,并不是完全开源的。
  2. Oracle JDK 比 OpenJDK 更稳定。OpenJDK 和 Oracle JDK 的代码几乎相同,但 Oracle JDK 有更多的类和一些错误修复。
  3. Oracle JDK 比 OpenJDK 更稳定。OpenJDK 和 Oracle JDK 的代码几乎相同,但 Oracle JDK 有更多的类和一些错误修复。
  4. Oracle JDK 不会为即将发布的版本提供长期支持,用户每次都必须通过更新到最新版本获得支持来获取最新版本。

80. Java 和 C++的区别?Java和C语言的区别?*

都是面向对象的语言,都支持封装、继承和多态。

  1. Java 不提供指针来直接访问内存,程序内存更加安全
  2. Java 的类是单继承的,C++ 支持多重继承;虽然 Java 的类不可以多继承,但是接口可以多继承。
  3. Java 有自动内存管理机制,不需要程序员手动释放无用内存
  4. 在 C 语言中,字符串或字符数组最后都会有一个额外的字符‘\0’来表示结束。但是,Java 语言中没有结束符这一概念。

Java和C的主要区别除了上述区别外,最主要的一点是:

  • 面向对象:从抽象封装继承多态的角度回答。
  • 面向过程:“面向过程”(Procedure Oriented)是一种以过程为中心的编程思想。这些都是以什么正在发生为主要目标进行编程。

81. 为什么说 Java 语言“编译与解释并存”?

因为Java是先编译成字节码,然后字节码由 Java 解释器来解释执行,对于热点代码还会翻译成机器码以提高效率。

高级编程语言按照程序的执行方式分为编译型和解释型两种。简单来说,编译型语言是指编译器针对特定的操作系统将源代码一次性翻译成可被该平台执行的机器码;解释型语言是指解释器对源程序逐行解释成特定平台的机器码并立即执行。
比如,你想阅读一本英文名著,你可以找一个英文翻译人员帮助你阅读, 有两种选择方式,你可以先等翻译人员将全本的英文名著(也就是源码)都翻译成汉语,再去阅读,也可以让翻译人员翻译一段,你在旁边阅读一段,慢慢把书读完。

82. 标识符和关键字的区别是什么?*

在我们编写程序的时候,需要大量地为程序、类、变量、方法等取名字,于是就有了标识符,简单来说,标识符就是一个名字。但是有一些标识符,Java 语言已经赋予了其特殊的含义,只能用于特定的地方,这种特殊的标识符就是关键字。因此,关键字是被赋予特殊含义的标识符。比如,在我们的日常生活中 ,“警察局”这个名字已经被赋予了特殊的含义,所以如果你开一家店,店的名字不能叫“警察局”,“警察局”就是我们日常生活中的关键字。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

面试准备:Java常见面试题汇总(二) 的相关文章

  • 面试准备

    首行占位 Python相关 1 Python3特性和变化 1 Python3默认编码是UTF 8 xff0c 2为ASCII 2 Python3中只有int xff0c 2中有int跟long xff0c 其中int不能超过sys maxi
  • Java并发编程实战——彻底理解volatile

    文章目录 volatile作用 volatile实现原理 volatile的happens before关系 volatile的内存语义 volatile重排序与JMM内存屏障 volatile的使用误区 volatile的适用场景 vol
  • IT公司智力题(持续跟新中)

    请听题 用赵本山在 买车 的语气 1 有1000瓶药物 但是其中有一瓶是有毒的 小白鼠吃了一个星期以后就会死掉 请问 在一个星期内找出有毒的药物 最少需要多少只小白鼠 解答 用二进制的思路去思考 1000瓶药代表了1000种状态 那么100
  • 编程技术面试的五大要点

    文 何海涛 扎实的基础知识 高质量的代码 清晰的思路 优化代码的能力 优秀的综合能力是编程技术面试的五大要点 找工作一直是一个热门话题 要想找到心仪的工作 难免需要经过多轮面试 编程面试是程序员面试过程中最为重要的一个环节 如果能在编程面试
  • 面试准备:Spring/Spring MVC常见面试题汇总

    文章目录 1 Spring框架有什么优点 2 什么是AOP 3 实现AOP的方式 AOP织入的三种时期 Spring AOP是怎么实现的 4 JDK动态代理实现方式 5 PageHelper实现方式 6 什么是IoC 什么是DI 7 Spr
  • 面试准备:操作系统常见面试题汇总

    文章目录 1 为什么要有用户态和内核态 内核态和用户态的运作方式 2 进程间通信方式介绍 3 Linux查看进程状态 cpu状态 占用端口的进程号的命令 linux top命令VIRT RES SHR DATA的含义 4 什么是Swap 5
  • 面试准备:Mybatis常见面试题汇总

    文章目录 1 和 的区别是什么 2 当实体类中的属性名和表中的字段名不一样 怎么办 3 模糊查询like语句该怎么写 4 Mybatis 一对一 一对多的xml怎么写 5 Dao 接口的工作原理是什么 Dao 接口里的方法 参数不同时 方法
  • 面试总结:测试常见面试题汇总

    文章目录 理论 测试流程 各个测试阶段 单元测试 集成测试 系统测试区别 测试用例设计 什么是好的测试用例 方法 用户登录 实例 App测试和Web测试的区别 典型测试场景 聊天功能测试用例怎么设计 怎么测试微信朋友圈 TODO 怎么测试微
  • 面试准备:Java新特性详解

    文章目录 Java语言新特性 1 Lambda表达式和函数式接口 2 接口的默认方法和静态方法 3 方法引用 4 重复注解 5 更好的类型推断 6 拓宽注解的应用场景 Java编译器新特性 参数名称 JVM的新特性 更多资料 参考 java
  • 面试准备:Java常见面试题汇总(一)

    面试准备 Java常见面试题汇总 一 面试准备 Java常见面试题汇总 二 面试准备 Java常见面试题汇总 三 文章目录 1 面向对象的特点 特性有哪些 补充 Java的多态是编译时多态还是运行时多态 2 接口和抽象类的相同点和不同点 3
  • 面试题目总结(CNN)

    CNN权值共享是什么 局部感知 即网络部分连通 每个神经元只与上一层的部分神经元相连 只感知局部 而不是整幅图像 滑窗实现 可行性 局部像素关系紧密 较远像素相关性弱 因此只需要局部感知 在更高层将局部的信息综合起来就得到了全局的信息 权值
  • Java架构直通车——以JDBC为例谈双亲委派模型的破坏

    文章目录 引入 JDBC4 0之前 JDBC4 0之后 引入 java给数据库操作提供了一个Driver接口 public interface Driver Connection connect String url java util P
  • 面试准备:Java常见面试题汇总(二)

    面试准备 Java常见面试题汇总 一 面试准备 Java常见面试题汇总 二 面试准备 Java常见面试题汇总 三 文章目录 43 java 中的 Math round 1 5 等于多少 44 String str abc 与 String
  • Java架构直通车——Java基础面试考点清单

    文章目录 基础 J U C jvm虚拟机 数据结构 算法 Spring RPC通信框架 网络通信 MQ 缓存 Mybatis 其他技术 基础 强引用 弱引用 虚引用 软引用 final关键字的作用 方法 变量 类 泛型 泛型继承 泛型擦除
  • Java架构直通车——深入理解B+树

    文章目录 引入 AVL树和B树 AVL树 红黑树 B树 B 树 数据库为什么不使用二叉树 为什么使用B 树 与B树的区别 引入 AVL树和B树 AVL树 平衡二叉搜索树是基于二分法的策略提高数据的查找速度的二叉树的数据结构 平衡二叉搜索树的
  • 从头开始学Java——JVM虚拟机八问

    文章目录 什么是Java虚拟机 为什么Java被称为 平台无关的编程语言 什么是JIT HotSpot怎么工作的 HotSpot虚拟机要使用解释器与编译器并存的架构 什么是编译时 运行时 编译 运行 编译时运行时问题归纳 反射 描述Java
  • 招银网络科技电话面试

    1 关于项目的负责内容 还是非常有必要熟悉应急 天基的基础传输模块的 基本面试中都会觉得只界面模块很单薄 应急 基础传输模块 无人机网络协议 速率控制模块 界面模块 天基 基础传输模块 MRUDP 界面模块 2 TCP长连接 问 如何在TC
  • 测开面经总结的经常考察的知识点

    一 算法相关 1 熟悉常见的排序算法 冒泡排序 插入排序 选择排序 归并排序 堆排序 快排 希尔排序 二 计算机网络相关 1 http协议 http 超文本传输协议 是一个在客户端和服务器端之间基于请求与响应模式的 无状态的 应用层的协议
  • 【动态规划】最长公共子序列和最长公共子串(python)

    编写用时 2020年3月12日12 02 28 1h 动态规划经典例题 最长公共子序列和最长公共子串 python 很久之前大概是高中的时候写过这种题目 使用动态规划的方法求解的 现读研究生了 要把过去的拾起来的呢 1 最长公共子序列 LC
  • Linux内核内存管理算法Buddy和Slab

    文章目录 Buddy分配器 CMA Slab分配器 总结 Buddy分配器 假设这是一段连续的页框 阴影部分表示已经被使用的页框 现在需要申请一个连续的5个页框 这个时候 在这段内存上不能找到连续的5个空闲的页框 就会去另一段内存上去寻找5

随机推荐

  • Ps2021软件安装Mac(含M1)、Windows版

    一 下载链接 Mac版本 1 M1芯片适用 百度网盘下载地址 提取码 rpyn 2 M1不适用 系统10 15及以上 下载地址 Windows版本 Ps2022 64位 此版本不支持LTSC版本系统 百度网盘下载地址 提取码 wb2d 二
  • MySQL: 运算符使用练习

    前言 练习运算符的使用 加强记忆 案例目的 在已建数据库中创建数据表 并对表中数据进行处理 练习运算符 包括数据运算符 逻辑运算符 位运算符 的使用 操作过程 创建名为 tb15 的数据表 包含 VARCHAR 100 类型的字段 note
  • 在vue中使用this.$refs报错undefined找不到子组件的方法

    最近遇见个问题 在父组件调用子组件的方法时报错undefined 这种情况一般都是父组件调用子组件方法的时候 dom还没更新成功 可以使用 this nextTick 这个方法作用是当数据被修改后使用这个方法会回调获取更新后的dom再渲染出
  • Spring MVC视图解析器

    Spring MVC视图解析器 AbstractCachingViewResolver XmlViewResolver ResourceBundleViewResolver UrlBasedViewResolver InternalReso
  • 报错Caused by java lang ClassNotFoundException Didn't find c

    分享一下我老师大神的人工智能教程 零基础 通俗易懂 风趣幽默 还带黄段子 希望你也加入到我们人工智能的队伍中来 https blog csdn net jiangjunshow 报错信息 03 14 20 24 09 675 15801 1
  • 赛码-接金币-java

    题目描述 小赛非常喜欢玩游戏 最近喜欢上了一个接金币的游戏 在游戏中 使用帽子左右移动接金币 金币接的越多越好 但是金币掉到地上就不能再接了 为了方便问题的描述 我们把电脑屏幕分成11格 帽子每次能左右移动一格 现在给电脑屏幕如图标上坐标
  • 浅谈systemd

    浅谈systemd systemd的基本概念 Systemd的管理服务 Systemd的管理运行级别 systemd的基本概念 一 system的简要介绍 定义 系统启动和服务器守护进程管理器 负责在系统启动或运行时 激活系统资源 服务器进
  • 使用Docker安装Portainer

    打开powershell输入命令 docker run d p 9000 9000 restart always v var run docker sock var run docker sock name prtainer portain
  • 双轮平衡车实现自平衡功能

    1 功能说明 在双轮小车上安装一个六轴陀螺仪传感器 本文示例将实现双轮小车自主平衡功能 2 电子硬件 在这个示例中 我们采用了以下硬件 请大家参考 主控板 Basra主控板 兼容Arduino Uno 扩展板 Bigfish2 1扩展板 传
  • 【Java】Java中的“引用类型”和C中的“指针”区别

    文章目录 前言 1 Java中 基本数据类型 和 引用数据类型 变量在内存分配时的不同 2 C中 指针 的特点 3 Java中 引用 的特点 4 Java的参数传递 5 参考 前言 在学习Java中变量的数据类型时 发现其分为2大类 基本数
  • spring加载流程之ConfigurationClassPostProcessor

    spring加载流程之ConfigurationClassPostProcessor ConfigurationClassPostProcessor postProcessBeanDefinitionRegistry processConf
  • FPGA面试真题解析

    FPGA面试真题解析 1 1 十进制46 25对应的二进制表达式为 硬件逻辑实习岗 A 101110 11 B 101101 01 C 101110 1 D 101110 01 解析 这个问题看上去很简单 那是因为我们平时可以打开电脑上的计
  • 若依-了解头像(文件)上传流程

    周知 本文主要记录本人关于自主学习若依框架的部分心得经验 必定有许多不足甚至理解有误的部分 如果对你有帮助我也不胜欢喜 如果发现有错误的地方也希望能够分享指出 一起加油 需求原因 客户内部系统 一般不对外开放 因此不适合将文件 如图片 存放
  • 常用的函数接口

    常用的函数接口 FunctionalInterface public interface Function
  • R语言 if else 语句

    R语言中if else语句的编写格式 因为R是解释语言 如果else单独起一行 无法解释执行 所以else不能单独一行 最好这样写 if a print hello else print Hi 转载于 https www cnblogs c
  • 基于vue+element-ui实现Cascader级联选择器+Table树形数据

    开发进度提前50 啊 真香 下面 看图说话 Table树形数据 Cascader级联选择器 功能实现 详细代码 Cascader级联选择器 options属性指定选项数组即可渲染出一个级联选择器 所以后端接口返回的数据结构要保持一一致性 这
  • 记一次Redis批量删除Key问题

    记一次Redis批量删除Key问题 前言 最近在项目中使用redis时发现一个问题 批量删除的时候删除不了 代码如下 redis配置 Bean public RedisTemplate redisTemplate RedisConnecti
  • C/C++中关于位域的一些总结

    转载自 http blog csdn net xkjcf article details 7688528 由于信息存储时 可能只占一位或者几位二进制位 比如开关量 只需要占据一位即可 为了节省存储空间 并且处理简单 C语言提供了一种数据结构
  • 点云地图三维表面重建

    通过对点云进行表面三角化mesh重建 可以使得点云地图更加轻量化 同时针对地面 红色 和非地面 蓝色 使用不同采样率的三角面片顶点 可以进一步减少地图数据量
  • 面试准备:Java常见面试题汇总(二)

    面试准备 Java常见面试题汇总 一 面试准备 Java常见面试题汇总 二 面试准备 Java常见面试题汇总 三 文章目录 43 java 中的 Math round 1 5 等于多少 44 String str abc 与 String