一。从内部类到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())
十。图片知识点-面试准备
String final修释 一堆常量
StringBuilder 无final 可以修改
StringBuffer 加sychronized 线程安全