机会是留给有准备的人!

  1. 简单说下什么是跨平台?
    由于各种操作系统指令集不是完全一致的,所有在操作系统之上加个虚拟机可以来提供统一接口,屏蔽系统之间的差异。

  2. java有几种基本数据类型

    数据类型 字节 默认值
    byte 1 0
    short 2 0
    int 4 0
    long 8 0
    float 4 0.0f
    double 8 0.0d
    char 2 '\u0000'
    boolean 4 false
  3. ArratList和LinkedList区别
    ArrayList是基于动态数组的数据结构,LinkedList是基于链表的数据结构。
    对于随机访问get和set,ArrayList优于LinkedList,因为LinkedList要移动指针。
    对于新增和删除add和remove,LinkedList优于ArrayList,因为ArrayList要移动数据。

  4. ConncurrentModificationException异常出现的原因

      public class Test{
     	 public static void main(String[] args){
     		 ArrayList<Integer> list = new ArrayList<Integer>();
     		 list.add(2);
     		 Iterator<Integer> ite = list.iterator();
     		 while(ite.hasNext()){
     			 Integer integer = ite.next();
     			 if(integer == 2){
     				 list.remove(integer);
     			 }
     		 }
     	 }
      }
    

    执行上段代码是有问题的,会抛出ConcurrentModification异常。
    原因:调用list.remove()方法导致modCount和expectedModeCount的值不一致。

    final void checkForComodication(){
        if(modCount != expectedModCount
       	 throw new ConcurrentModification();
    }
    

    解决方法:在迭代器中如果要删除元素的话,需要调用Iterator类的remove方法。

    public class Test{
        public static void main(String[] args){
       	 ArrayList<Integer> list = new ArrayList<Integer>();
       	 list.add(2);
       	 Iterator<Integer> ite = list.iterator();
       	 while(ite.hasNext()){
       		 Integer integer = ite.next();
       		 if(integer == 2){
       			 ite.remove();//注意这个地方
       		 }
       	 }
        }
    } 
    
  5. String、StringBuffer和StringBuilder区别

    1. 数据可变和不可变
      String底层使用一个不可变的字符数组private final char value[];所以它内容不可变;
      StringBufferStringBuilder都继承了AbstractStringBuilder底层使用的是可变字符数组:char[] value;
    2. 线程安全
      StringBuilder是线程不安全的,效率较高;而StringBuffer是线程安全的,效率较低。
      通过他们的append()方法来看,StringBuffer是有同步锁,而StringBuilder没有:
    @Override
    public sychronized StringBuffer append(Object obj){
    	toStringCache = null;
    	super.append(String.valueOf(obj));
    	return this;
    }
    
    @Override
    public StringBuilder append(String str){
    	super.append(str);
    	return this;
    }
    
    1. 相同点
      StringBuilderStringBuffer有公共父类`AbstractStringBuilder'。

    最后,操作可变字符速度:StringBuilder > StringBuffer > String

  6. HashMap和HashTable、ConcurrentHashMap区别?
    相同点:
    1.HashMap和HashTable都实现Map接口
    2.都可以存储key-value数据
    不同点:
    1.HashMap可以将null作为key或value,HashTable不可以。
    2.HashMap是线程不安全,效率高,HashTable是线程安全,效率低。
    3.HashMap的迭代器(Iterator)是fail-fast迭代器,而HashTable的迭代器(enumerator)不是fail-fast。

    fail-fast:就是就快的时间能把错误抛出而不是让程序执行。

    如何保证线程安全又效率高?
    Java5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。
    我们能否让HashMap同步?
    HashMap可以通过下面的语句进行同步:
    Map m = Collections.sychronizeMap(hashMap);

  7. 线程创建方式

    1. 继承Thread类,作为线程对象存在(继承Thread对象)
    public class CreatThreadDemo1 extends Thread{
    /**
     * 构造方法: 继承父类方法的Thread(String name);方法
     * @param name
     */
    public CreatThreadDemo1(String name){
        super(name);
    }
    
    @Override
    public void run() {
        while (!interrupted()){
            System.out.println(getName()+"线程执行了...");
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) {
        CreatThreadDemo1 d1 = new CreatThreadDemo1("first");
        CreatThreadDemo1 d2 = new CreatThreadDemo1("second");
    
        d1.start();
        d2.start();
    
        d1.interrupt();  //中断第一个线程
    }
    }
    

    常规方法,不多做介绍了,interrupted方法,是来判断该线程是否被中断。(终止线程不允许用stop方法,该方法不会施放占用的资源。所以我们在设计程序的时候,要按照中断线程的思维去设计,就像上面的代码一样)。

    1. 实现runnable接口作为线程任务存在
    public class CreatThreadDemo2 implements Runnable {
    @Override
    public void run() {
        while (true){
            System.out.println("线程执行了...");
        }
    }
    
    public static void main(String[] args) {
        //将线程任务传给线程对象
        Thread thread = new Thread(new CreatThreadDemo2());
        //启动线程
        thread.start();
    }
    }
    

    Runnable 只是来修饰线程所执行的任务,它不是一个线程对象。想要启动Runnable对象,必须将它放到一个线程对象里。

    1. 匿名内部类创建线程对象
    public class CreatThreadDemo3 extends Thread{
    public static void main(String[] args) {
        //创建无参线程对象
        new Thread(){
            @Override
            public void run() {
                System.out.println("线程执行了...");
            }
        }.start();
       //创建带线程任务的线程对象
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程执行了...");
            }
        }).start();
        //创建带线程任务并且重写run方法的线程对象
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("runnable run 线程执行了...");
            }
        }){
            @Override
            public void run() {
                System.out.println("override run 线程执行了...");
            }
        }.start();
    }
    }
    

    创建带线程任务并且重写run方法的线程对象中,为什么只运行了Thread的run方法。我们看看Thread类的源码:
    public class Thread implements Runnable{……};
    4.创建带返回值的线程

    public class CreatThreadDemo4 implements Callable {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CreatThreadDemo4 demo4 = new CreatThreadDemo4();
    
        FutureTask<Integer> task = new FutureTask<Integer>(demo4); //FutureTask最终实现的是runnable接口
    
        Thread thread = new Thread(task);
    
        thread.start();
    
        System.out.println("我可以在这里做点别的业务逻辑...因为FutureTask是提前完成任务");
        //拿出线程执行的返回值
        Integer result = task.get();
        System.out.println("线程中运算的结果为:"+result);
    }
    
    //重写Callable接口的call方法
    @Override
    public Object call() throws Exception {
        int result = 1;
        System.out.println("业务逻辑计算中...");
        Thread.sleep(3000);
        return result;
    }
    }
    

    Callable接口介绍:

    public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
    }
    

    返回指定泛型的call方法。然后调用FutureTask对象的get方法得道call方法的返回值。

    1. 定时器Timer
    public class CreatThreadDemo5 {
    
    public static void main(String[] args) {
        Timer timer = new Timer();
    
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("定时器线程执行了...");
            }
        },0,1000);   //延迟0,周期1s
    
    }
    }
    
    1. 线程池创建线程
    public class CreatThreadDemo6 {
    public static void main(String[] args) {
        //创建一个具有10个线程的线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(10);
        long threadpoolUseTime = System.currentTimeMillis();
        for (int i = 0;i<10;i++){
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"线程执行了...");
                }
            });
        }
        long threadpoolUseTime1 = System.currentTimeMillis();
        System.out.println("多线程用时"+(threadpoolUseTime1-threadpoolUseTime));
        //销毁线程池
        threadPool.shutdown();
        threadpoolUseTime = System.currentTimeMillis();
    }
    
    }
    

    7.利用java8新特性 stream 实现并发

    public class CreatThreadDemo7 {
    public static void main(String[] args) {
        List<Integer> values = Arrays.asList(10,20,30,40);
        //parallel 平行的,并行的
        int result = values.parallelStream().mapToInt(p -> p*2).sum();
        System.out.println(result);
        //怎么证明它是并发处理呢
        values.parallelStream().forEach(p-> System.out.println(p));
    		}
    	}