2021-02-07面试笔试一篇通

一。从内部类到lambda表达式+包装类装箱拆箱

//外部类
public class testDeque {
    
    
    int num=100;

    //内部类
    class A{
    
    
        int num=10;
        public void put(){
    
    
            int num=1;
            System.out.println("inner");
            //num为方法中的    为了区分,使用 类名.this.变量名
            System.out.println(num+" "+this.num+" "+testDeque.this.num);
        }

        public void put2(){
    
    
            final int a=1;
//          局部内部类
            class B{
    
    
                public void put(){
    
    
//                  只能使用方法final的变量,不能改变
                    System.out.println(a);
                }
            }
        }
    }

//    Good接口
//    public interface Good {
    
    
//        int put(int a);
//    }

    public static void main(String[] args) {
    
    
        //匿名内部类实现接口
        Good good1 = new Good() {
    
    
            @Override
            public int put(int a) {
    
    
                return a;
            }
        };

        //lambda表达式简化匿名内部类  ()->{  }
        Good good2=(int a)->{
    
     return a; };

        //lambda表达式单个参数简写方式
        Good good=a->{
    
     return a; };
        
//------------------------------------------------
        //装箱
        Integer integer1 = Integer.valueOf(1);

        //自动装箱
        Integer integer=1;
        //拆箱
        int a=integer.intValue();
        //Integer转String
        String s = integer.toString();
        System.out.println(s+1);
        //11

        //转int
        int aa=Integer.parseInt(s);
        System.out.println(aa+1);
        //2
    }
}


二。Java锁机制-面试准备

一。悲观锁和乐观锁是两种思想

悲观锁:(会影响效率)
【真的加锁,不管有没有持有资源都会加锁,生怕出事】

1.独占锁
无论是读还是写,有且仅有一个资源可以进行。

①sychronized

②Lock接口下的ReentrantLock

乐观锁: (比较快)
【实际上不加锁,他相信在用某一种方式进行更新的时候是保证线程安全的,
因为执行更新前会检查内存中和对象的属性是否一致。】

1.允许多个执行读操作的线程同时访问共享资源。

①CAS
Unsafe的CAS原子操作(在OS中的)
例如 ConcurrentHashMap

二。可重入锁

①sychronized

②Lock接口下的ReentrantLock

三。分段锁

ConcurrentHashMap

四。公平锁 和 非公平锁

1.公平锁维护一个队列(效率低),加锁前按队列排队来进行

2.非公平锁直接尝试CAS获取锁,若不成则直接放后面
①sychronized

②Lock接口下的ReentrantLock

五。自旋锁

​ 自旋锁原理非常简单,如果持有锁的线程能在很短时间内释放锁资源,那么那些等待竞争锁 的线程就不需要做内核态和用户态之间的切换进入阻塞挂起状态,它们只需要等一等(自旋), 等持有锁的线程释放锁后即可立即获取锁,这样就避免用户线程和内核的切换的消耗。


三。Java注意事项

1.LinkedList不能用多态,若使用则无法使用子类方法 (List list=new LinkedList(); )

2.set.for//增强for快捷遍历

3.new String().var //快捷创建

4.native方法 调用本地操作系统的方法

5.hashcode 返回十进制整数,tostring方法也是返回对应的十六进制数

6.hashcode的逻辑地址,不是实际的物理地址

7.map遍历方式
获取key到set中 遍历set时map.get key

或者使用Entry对象set集合
Set<Map.Entry<String,String> > set=map.entrySet();

8.重写hashCode方法 和equals方法(会直接指定的不需要手动写)

都是调用Object.hash(1,2);
Object.equals(1,2)


四。jdkAPI Util包 集合 阅读

Collection
List:
增加了索引操作,在增删查的时候
增加了subList(int fromIndex, int toIndex) 返回此列表之间的fromIndex(包括)和toIndex之间的独占视图。

ArrayList:
增加了clone()操作。 返回此 ArrayList实例的浅拷贝。
增加了indexOf(Object o) lastIndexOf(Object o)

Stack:
就这几个

        Stack stack=new Stack();
        stack.push(1);
        stack.peek();
        stack.pop();
        stack.empty();

相对于Collection

Set:啥也没加

HashSet:
加了增加了clone()操作。 返回此 HashSet实例的浅层副本:元素本身不被克隆。


Queue:

        Queue queue=new LinkedList();
        queue.add(1);
        queue.peek();
        queue.poll();
        queue.offer(2);
        queue.element();

ArrayQueue:
所有操作增加了First和Last

PriorityQueue:

  Queue queue=new PriorityQueue();
        queue.add(1);
        queue.offer(2);
        queue.peek();
        queue.element();
        queue.poll();

剩余:
LinkedList
TreeSet
Map


五。Collection接口的常用方法演示

package com;

import java.util.*;

public class testAll {
    
    
    public static void main(String[] args) {
    
    
        //多态创建只能使用父亲的方法(例如:lingkedlist无法使用头尾插入的操作)
        Collection collection=new Vector();
        Collection collection1=new Stack();
        Collection collection2=new ArrayList();
        Collection collection3=new LinkedList();
        //这也是多态创建
        List list=new LinkedList();
        List list1=new ArrayList();
        List list2=new Vector();
        List list3=new Stack();
        //这也是多态创建
        Vector vector=new Stack();


        ArrayList arrayList=new ArrayList();
        LinkedList linkedList=new LinkedList();
        Vector vector1=new Vector();
        Stack stack=new Stack();



        vector.add(2);
//------------------------------------------------------------
        //以下是Collection的常见方法
        collection.add(1);
        collection.add("A");
        collection.add("A");
        //添加vector中所有元素
        collection.addAll(vector);
        //打印所有元素
        System.out.println("All elements:"+collection.toString());
        //返回集合的hashcode
        System.out.println("hashcode: "+collection.hashCode());
        //集合中是否存在1元素
        System.out.println("是否存在1:"+collection.contains(1));
        //包含关系 collection({1,"A","A"})是否包含collection1(空集)
        System.out.println(collection.containsAll(collection));
        //包含关系 collection1(空集)是否包含collection({1,"A","A"})
        System.out.println(collection1.containsAll(collection));
        //判空和大小
        System.out.println(collection.isEmpty()+" "+collection.size());
        System.out.println(collection.isEmpty()+" "+collection.size());
        //删除第一个出现的指定元素
        collection.remove("A");
        System.out.println(collection.isEmpty()+" "+collection.size());
        collection.remove("A");
        System.out.println(collection.isEmpty()+" "+collection.size());

        System.out.println(collection.toString());
        collection2.add(1);
        collection2.add(2);
        //删除与collection2的交集,通俗的说就是删掉collection2中含有的元素
        collection.removeAll(collection2);
        System.out.println(collection.toString());
        
        //下面演示转换成固定大小的数组
        Object[] objects=new Object[collection.size()];
        System.out.println("1:");
        for (Object object : objects) {
    
    
            System.out.println(object);
        }
        //第一种:直接接收
        objects=collection.toArray();
        //第二种,直接传数组引用
        collection.toArray(objects);
        System.out.println("2:");
        for (Object object : objects) {
    
    
            System.out.println(object);
        }
        
    }
}


六。HashMap面试准备

你了解过那些Map集合?

HashMap 、LinkedHashMap、ConcurrentHashMap


讲讲jdk1.7和1.8的区别 HashMap的区别:

总的来说,1.7的HashMap的底层是数组+链表,而1.8的HashMap的底层是数组+链表或红黑树。HashMap是线程不安全的。


讲讲初始化的方式:

初始化的方式有三种:(参数有两个,其一是capacity容量,其二是加载因子)
首先,设计者给了两个参数默认的值 :capacity容量为16和加载因子为0.75。
①无参构造:直接使用两个默认值进行构造
②capacity容量:加载因子使用默认值
③双参数构造法:capaciry如果超过Max值,那么会使用Max值,capacity小于0会抛异常,而加载因子小于0也会抛异常。


先来个简单的吧,讲讲Hash1.8的get方法

get方法调用的是getNode方法,参数是key的哈希值和key,看返回的是不是null值,不是说明有元素,则返回相应的value,否则返回null。
那么getNode方法:
先判断表是否存在,表有没有元素,hash的位置有没有元素
然后check hash的第一个元素
接着判断引用是否相等,然后equals是否相等

都没有的话就获取到下一个节点。
判断是RBTree 还是LinkedList (1.7的话就是直接搜链表了)
进行不同的遍历

get到就直接break返回了。


再讲讲HashMap jdk1.8put方法:

put方法调用putval
判断是RBTree还是链表:
链表尾插(1.7头插法)

判断是否到达阈值,进行数组扩容
判断一条链是否到达9个元素,进行转换RBTree操作
(有判重操作)

那再说说HashMap1.8扩容的思路吧:

扩容是这样,hash地址值和Oldcapacity进行与操作,结果只有1和0两种结果,因为扩容是两倍增长,例如原来size为16那么下表0-15,一个hash为0,那么扩容后为32,下标为0-31那么原来0的位置的链表有两个去处,其一是0(原地不动),其二是0+16oldcapacity=16,那么0和1就是起到这个效果,剪短链表。那么红黑数也差不多,他不仅有红黑数的特征,还是一个双向链表,
不一样的是,RBTree分成两段后,会进行一系列的判断,例如是否左边没有元素 或者右边没有元素 那么直接移动整棵树,提高效率,又或者是分成两段后的个数是否<=6个,如果是就转换回链表结构。

(1.7的话就简单许多,就是直接链表头插法就完事了,特点是每次扩容数据都会倒置)


ConcurrentHashMap了解过吗 说说看

这个是线程安全的 ,而且1.7和1.8的差别很大
总的来说,1.7 使用ReentrantLock ,而1.8采用synchronized 关键字。
1.7
Unsafe中的方法关键之一
CAS compareAndSafe
OS层的方法,原子操作
== 准备操作前还会不停的获取内存的元素和Obj的比较进行check

只有当这两个是一致的时候才会执行原子操作
按我的理解是分为三层结构:
①segment层
②hashentry层
③Link层

构造法是有三个参数(segment size,factor,hashentry size)
默认是16 0.75 16
segment的个数的固定的,不会进行扩容
而hashentry会进行扩容,
一个segment对应的hashentry 或者叫管理的个数是 hashentry.size %segment.size 上取整。
由默认推出最少的情况是1:1的比例,但是实际上是1:2。
然后扩容的时候,是互不相干的,扩容只会扩容对应segment下的hashentry数组。 与hash链表迁移方式大致一致吧。

put 和get方法的话也是先用key找到segment 然后在对应segment上找到对应的hashentry位置,查找链表或插入。
(有判重操作)

1.8
不再使用segment层
不太恰当的理解就是每个数组下表相当于一个segment ,对每个位置进行加锁。
为null时用unsafe的cas保证并发安全
不为null时使用synchronized关键字


七。List面试准备

你了解过那些List集合?

ArrayList、LinkedList 、Vector

讲讲ArrayList吧

ArrayList 底层是一个数组。
构造器三种:
①无参构造,初始化一个默认大小为10的数组。
②给一个capacity初始化为这个大小。
③给定另一个已有的集合A,调用A的toArray方法,A如果为0那么也按无参构造处理,
如果A中有元素那么就调用Array.copyOf函数复制过来。

add的话就是跟普通数组一样,多了扩容的判断与操作。
真正add前先判断扩容

		//1.5倍扩容   jdk 1.8
        int newCapacity = oldCapacity + (oldCapacity >> 1);

如果是用索引add 那么会调用native的移动数组方法

    public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);

特点就是随机访问快,在中间插入效率低下。


LinkedList也说说吧,底层是怎么实现的

LinkedList是一个双向链表,
初始化两种方式:
①空构造
②使用一个存在的集合。调用addAll方法先toArray成Object数组 然后进行尾插法,插入前经过强转LinkedList给的泛型。

采用尾插法

for (Object o : a) {
    
    
            @SuppressWarnings("unchecked") E e = (E) o;
            Node<E> newNode = new Node<>(pred, e, null);
            if (pred == null)
                first = newNode;
            else
                pred.next = newNode;
            pred = newNode;
        }

在头部和尾部get元素的和删除的如果不存在都抛异常。

如果是get一个index,会先进入一个判断方法判断,不在范围也是抛异常

public E getFirst() {
    
    
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return f.item;
    }

    /**
     * Returns the last element in this list.
     *
     * @return the last element in this list
     * @throws NoSuchElementException if this list is empty
     */
    public E getLast() {
    
    
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return l.item;
    }

    /**
     * Removes and returns the first element from this list.
     *
     * @return the first element from this list
     * @throws NoSuchElementException if this list is empty
     */
    public E removeFirst() {
    
    
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return unlinkFirst(f);
    }

    /**
     * Removes and returns the last element from this list.
     *
     * @return the last element from this list
     * @throws NoSuchElementException if this list is empty
     */
    public E removeLast() {
    
    
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return unlinkLast(l);
    }

四种add方法:
①add一个元素:调用尾插法
②addfirst方法:调用头插法
③addlast方法:调用尾插法
④add index,key:先判断范围,超范围抛异常,再看是不是最后一个位置,如果是那就直接调用尾插法,否则的话就找吧。

offer的话都是调用add方法,尾插 ,最后返回一个true。
push是调用addFirst和pop 调用removeFirst,都是对头进行操作

Vector呢,是线程安全的吗?

是的 几乎所有方法都使用了synchronized关键字

Vector是怎么扩容的?

扩容两倍增长,如果没有指定,数组进行迁移


八。多线程面试准备

磁盘存储360,双击打开,去到内存中,使用功能(病毒查杀),开启线程(线程是通往cpu的路径),杀毒过程中使用清理垃圾,又一个线程开启,现在就是多线程,再次打开一条通往cpu的路,cpu高速切换两条路。

java程序是单线程程序,
先是主线程main开启(jvm找一条路通往cpu,即开启一个栈),
然后进行方法压栈执行,但是遇到线程开启,则又开启一个栈,供cpu高速切换。

java实现多线程程序的两种方式:
①继承Thread 重写 run方法
②实现Runnable接口 的run方法

    //继承Thread  继承的单一性    耦合度高   且无法使用lambda表达式简化
        new Thread(){
    
    
            @Override
            public void run(){
    
    
                System.out.println("start");
            }
        }.start();

        //实现runnable接口 避免继承单一性 解耦  可以使用java8 新特性lambda表达式简化代码
        new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                System.out.println("runnable start");
            }
        }).start();


        //lambda表达式简化
        new Thread(()->System.out.println("runnable start")).start();

        new Thread( ()->System.out.println("1") ).start();

//        -----------------------通过线程池的方式获取----------------------------------------------------
        
        //通过Executors的newFixedThreadPool方法 参数一个就是线程的数量。返回一个ExecutorService接收
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
        
        //直接通过ExecutorService的submit方法,传入一个Runnable就直接开启了。
        fixedThreadPool.submit(()-> System.out.println("start"));
        
        //使用shutdown直接停止 ,不建议用这个
        //fixedThreadPool.shutdown();

多线程安全问题和使用synchronized保证安全

package com;

public class store implements Runnable{
    
    
   private String name;
   private double income=0f;
   private Object object=new Object();
   //synchronized如果修饰类或者静态方法,那么锁是整个类


   //方法二:直接用synchronized修饰方法,锁是对象
    @Override
    public synchronized void run(){
    
    

        for(int i=1;i<=20;i++){
    
    

            try {
    
    
                Thread.sleep(1000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }

            //方式一:代码块synchronized
            //加synchronized关键字即可保证线程安全  锁是对象
//            synchronized (object){
    
    
                System.out.println("线程: "+Thread.currentThread().getName() +" 当前收入: "+ ++income);

//            }
        }
    }

    public static void main(String[] args) {
    
    
        //实现了Runnable接口的商店类对象
        store store=new store();
        //将他传入创建一个线程0
        Thread thread=new Thread(store);
        thread.start();
        //线程1
        Thread thread1=new Thread(store);
        thread1.start();
    }

}

使用Lock接口下的 ReentreantLock实现类

package com;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class store implements Runnable{
    
    
   private String name;
   private double income=0f;
   //使用Lock接口下的 ReentreantLock实现类获取一把锁
   Lock lock=new ReentrantLock();

   @Override
   public void run(){
    
    

       for(int i=1;i<=20;i++){
    
    
           //调用lock
           lock.lock();
           try {
    
    
               Thread.sleep(1000);
           } catch (InterruptedException e) {
    
    
               e.printStackTrace();
           }
           System.out.println(Thread.currentThread().getName()+"  "+ ++income);
           //unlock
           lock.unlock();
       }
   }

    public static void main(String[] args) {
    
    
        store store=new store();
        Thread thread=new Thread(store);
        thread.start();
        Thread thread1=new Thread(store);
        thread1.start();
    }

}

包子类

package com;

public class Baozi {
    
    
    public boolean flag=true;

    Baozi(){
    
    

    }
}

包子铺类

package com;

public class Baozipu implements Runnable{
    
    
    private Baozi baozi;

    Baozipu(Baozi baozi){
    
    
        this.baozi=baozi;
    }


    @Override
    public void run() {
    
    
        while(true) {
    
    
            synchronized (baozi) {
    
    

                if(!baozi.flag) {
    
    
                    try {
    
    
                    	//调用wait方法会释放锁并等待
                        baozi.wait();
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                }
                    System.out.println("包子铺做包子");
                    try {
    
    
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }

                    baozi.flag = false;
                    baozi.notify();
            }
        }
    }
}

吃货类

package com;

public class Chihuo implements Runnable{
    
    
    public Baozi baozi;

    Chihuo(Baozi baozi){
    
    
        this.baozi=baozi;
    }
    @Override
    public void run() {
    
    
        while(true) {
    
    
            synchronized (baozi) {
    
    

                if(baozi.flag) {
    
    
                    try {
    
    
                    	//调用wait方法会释放锁 并等待
                        baozi.wait();
                        
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                }

                    System.out.println("吃货吃包子--------------");
                    try {
    
    
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                    baozi.flag = true;
                    baozi.notify();
            }
        }
    }
}

测试生产者和消费者

package com;

public class testAll {
    
    
    public static void main(String[] args) {
    
    
        Baozi baozi=new Baozi();
        Thread thread=new Thread(new Baozipu(baozi));
        Thread thread1=new Thread(new Chihuo(baozi));
        thread.start();
        thread1.start();
    }
}




九。Java 3注解 和 反射 面试准备

Java注解

jdk目前提供了三种注解@Override、@Deprecated、@SuppressWarnnings

①@Override:用于标识方法,标识该方法属于重写父类的方法

②@Deprecated:用于标识方法或类,标识该类或方法已过时,建议不要使用

③@SuppressWarnnings(“all”):用于有选择的关闭编译器对类、方法、成员变量、变量初始化的警告

元注解 注解自定义注解的

Java反射机制

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

三种通过反射获取类名的方式:
①类名.class
②对象名.getClass();
③Class.forName( 包名.类名)

作用:
简单来说就是:
①获取类名getName()
②获取属性 getFilds
③获取构造器getConstructors()
④获取方法getMethods() 单个getMethod(name)

⑤调用方法invoke(c.newInstance())

Java博文讲的很好-传送门


十。图片知识点-面试准备

String final修释 一堆常量

StringBuilder 无final 可以修改

StringBuffer 加sychronized 线程安全

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


十一。

猜你喜欢

转载自blog.csdn.net/BOWWOB/article/details/113746639