Java面试题

来源:互联网 发布:asp.net开源商城源码 编辑:程序博客网 时间:2024/04/29 01:54

1 java常用集合继承结构

这里写图片描述

2 JVM中对象从创建到销毁的过程

可参考 帮助理解一些过程 Java基础知识总结(一)创建和销毁对象
在虚拟机遇到一条new的指令时,先会在常量池中定位这个类的符号引用,并且检查这个类是否被加载,如果没有,那么必须先执行加载过程,在这个类加载完成后,接下来虚拟机将为新生对象分配内存,对象所需的内存在类加载完成后就会完全确定,也就是从堆中划分出一个区域,如果堆中的内存是绝对完整的,那么采用指针碰撞的方式即可完成内存的分配,也就是移动指针的位置即可,如果堆的内存不是绝对完整的,那么虚拟机就必须维护一个表来记录空闲的区域,这种方法叫做空闲列表 采用哪种方法由java堆是否完整决定,而java堆是否完整由采用哪种垃圾收集算法决定,比如Serial、ParNew等带ComPact(整理)过程的收集器,采用指针碰撞,采用CMS这种基于Mark-Sweep算法的收集器时,采用空闲列表方式。

还有一个问题需要考虑,那就是在分配空间的时候即使是移动指针这种方式,也不是线程安全的,也可能产生线程安全问题,因此要解决这个问题,一种是对分配内存空间的动作进行同步操作,另一种是每个线程分配一个缓冲区,哪个线程要分配内存,就现在这个区域上分配,只有在这个缓冲区用完需要重新分配时,才需要同步操作。

对象内存分配完之后,虚拟机需要将分配到的内存空间都初始化为0

之后虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例,如何才能找到类的元数据信息、对象的哈希吗、对象的GC分代年龄等信息。这些信息存放在对象头中。
上面步骤完成后,在虚拟机的角度 对象已经创建完成,但在程序员的角度对象创建才刚刚开始,之后按照程序员的意愿,初始化一些值,之后这个对象才真正创建完成了

2.1 创建

如果想真正弄清楚对象初始化,而不是仅仅记住一些像成员变量的初始值这样的规则,我觉得应该了解一个类在第一个创建对象时是如何从字节码编程的可用的对象的。
在第一次使用一个类的时候,无论是显示加载一个类(Class.forName等)还是隐式加载一个类(A.staticVariable,new A())时,首先要有ClassLoader进行加载:
(1)ClassLoader首先通过类名定位到类文件的位置(通过classpath等),将字节码加载到内存,通过准备、字节码验证和resolve等环节将等到一个个Class对象,放到方法区中;
(2)在此之后就是类初始化,这是类中的静态变量和静态初始化器将按照位置顺序进行初始化工作,静态变量同样放在方法区中;
(3)如果你进行是实例创建的化,接下来的工作首先是在堆上分配内存了,具体的方法可能有指针碰撞和空闲列表;
(4)获得了内存空间后,首先全部置零,这也就是为什么类的成员变量会还有初始值的原因,之后如果指定了初始化值,同样这里也是按顺序进行的;
(5)最后将执行也就是我们定义使用的构造器来进行我们自定义的初始化过程了,这里就可以获得我们想要的对象实例的引用了。
所以在类中,各个部分的初始化顺序是:静态变量,静态初始化器(按位置顺序)——>非静态成员变量(按位置顺序)——>构造器;
说完了基本过程,我们来看看在Java中一些具体的类型是怎样进行初始化的。

2.2 销毁

涉及到GC,可以通过下面的语句理解一下。
程序运行过程中每次分配的对象 语言的runtime都会记录谁和谁的引用关系,当内存不够用或者特定的时机就会触发GC,这个时候 首先会暂定所有的线程,也就是stop the world,这个时候整个语言的执行权交给了GC程序,GC会收集当前 全部的 【全局变量】【每一个线程的运行栈中的对象】等等,这些对象都是当前直接可以访问的对象,这些对象的合集称之为【根集】。然后GC会从这个根集出发遍历整个程序分配的对象,【由于对象的相互引用关系GC是知道的,所以这个问题就简化成了一个图论的问题,】。这个过程中如果出现 GC扫描不到的对象,但是runtime中有分配这个对象。这种对象就是垃圾对象。【仔细想想 GC的根集扫描就是判断当前代码那些变量是可以访问到的,那些变量存在内存中但是对象的 句柄已经丢失,程序代码已经访问不到的】。至于这些对象是立马回收还是标记后回收,这个就看内存回收的策略了。有各种针对这个做优化的算法,有的会内存释放后整理内存,有的会做颜色标记。

3代码实现JDK的序列化和反序列化

Java基础学习总结——Java对象的序列化和反序列化

先定义PO, 定义一个Person类,实现Serializable接口

import java.io.Serializable;/** * <p>ClassName: Person<p> * <p>Description:测试对象序列化和反序列化<p> * @author xudp * @version 1.0 V * @createTime 2014-6-9 下午02:33:25 */public class Person implements Serializable {    /**     * 序列化ID     */    private static final long serialVersionUID = -5809782578272943999L;    private int age;    private String name;    private String sex;    public int getAge() {        return age;    }    public String getName() {        return name;    }    public String getSex() {        return sex;    }    public void setAge(int age) {        this.age = age;    }    public void setName(String name) {        this.name = name;    }    public void setSex(String sex) {        this.sex = sex;    }}

序列化和反序列化Person类对象

import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.text.MessageFormat;/** * <p>ClassName: TestObjSerializeAndDeserialize<p> * <p>Description: 测试对象的序列化和反序列<p> * @author xudp * @version 1.0 V * @createTime 2014-6-9 下午03:17:25 */public class TestObjSerializeAndDeserialize {    public static void main(String[] args) throws Exception {        SerializePerson();//序列化Person对象        Person p = DeserializePerson();//反序列Perons对象        System.out.println(MessageFormat.format("name={0},age={1},sex={2}",                                                 p.getName(), p.getAge(), p.getSex()));    }    /**     * MethodName: SerializePerson      * Description: 序列化Person对象     * @author xudp     * @throws FileNotFoundException     * @throws IOException     */    private static void SerializePerson() throws FileNotFoundException,            IOException {        Person person = new Person();        person.setName("gacl");        person.setAge(25);        person.setSex("男");        // ObjectOutputStream 对象输出流,将Person对象存储到E盘的Person.txt文件中,完成对Person对象的序列化操作        ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(                new File("E:/Person.txt")));        oo.writeObject(person);        System.out.println("Person对象序列化成功!");        oo.close();    }    /**     * MethodName: DeserializePerson      * Description: 反序列Perons对象     * @author xudp     * @return     * @throws Exception     * @throws IOException     */    private static Person DeserializePerson() throws Exception, IOException {        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(                new File("E:/Person.txt")));        Person person = (Person) ois.readObject();        System.out.println("Person对象反序列化成功!");        return person;    }}

4 多线程计算1+2+3+4.。。+n

碰到一个比较好的文章,先放这里
java并发编程–一道经典多线程题的2种解法

首先想到的是用fork/join框架。
参考文章:Java线程之fork/join框架

MyTask类

import java.util.concurrent.ExecutionException;import java.util.concurrent.RecursiveTask;public class MyTask extends RecursiveTask<Long> {    /**     *      */    private static final long serialVersionUID = 1L;    private long start;    private long end;    public MyTask(long start,long end){        super();        this.start = start;        this.end = end;    }    @Override    protected Long compute() {        long result  =0;        if(end-start >10000){            MyTask task1 = new MyTask(start, start+100);            MyTask task2 = new MyTask(start+101, end);            invokeAll(task1, task2);              try {                result =  task1.get()+task2.get();            } catch (InterruptedException e) {                e.printStackTrace();            } catch (ExecutionException e) {                e.printStackTrace();            }        }else{            result =  getSum(start, end);        }        return result;    }    private long getSum(long start,long end) {        long sum = 0;        for(long i = start;i<=end;i++){            sum +=i;        }        return sum;    }}

ForkJoinMain 类

import java.util.concurrent.ForkJoinPool;public class ForkJoinMain {    public final static ForkJoinPool mainPool = new ForkJoinPool();    public static void main(String[] args) {        MyTask task = new MyTask(1L,200000L);          long count = mainPool.invoke(task);          System.out.println("ForkJoin计算结果:"+count);        System.out.println("主程序计算结果:"+getSum(1L,200000L));    }    private static long getSum(long start,long end) {        long sum = 0;        for(long i = start;i<=end;i++){            sum +=i;        }        return sum;    }}

又想到一种可以练习多线程的方式: 可以用另一个线程计算返回结果 ,然后主线程负责监听什么时候返回,然后打印结果。使用Callable接口

public class ComputeNSumTask implements Callable<Integer>{    private int n = 0;    public ComputeNSumTask(int n){        this.n = n;    }    @Override    public Integer call() throws Exception {        return getSum();    }    private int getSum() {        int sum = 0;        for(int i = 1;i<n;i++){            sum +=i;        }        return sum;    }}public class MyTask {    public static void main(String[] args) {        ExecutorService executorService = Executors.newSingleThreadExecutor();        ComputeNSumTask task = new ComputeNSumTask(100);        Future<Integer> result = executorService.submit(task);        executorService.shutdown();        while (true) {            if (result.isDone()) {                try {                    System.out.println(result.get());                    break;                } catch (InterruptedException | ExecutionException e) {                    e.printStackTrace();                }            }        }    }}

5 常用的RPC技术有哪些,描述一次RPC调用的过程

RMI、Hessian、Dubbo Dubbox Dubbos Hetty

一些比较小的 如:Kepler

JAVA中几种常用的RPC框架介绍
你应该知道的 RPC 原理
轻量级分布式 RPC 框架
Java利用Sping框架编写RPC远程过程调用服务的教程

6 设计一下购物车的UML类图

参考文章:毕业设计超市系统(一)UML 建模

7列出一下使用过的性能优化方案

待续。。。。

0 0
原创粉丝点击