笔记笔记笔记

java
基础
基础
特性
简单,面向对象,可移植,解释执行(先编译为class文件,然后jvm解释执行)
修饰符
作用域
private(同类)、protected(同包和子类)、default(同包)、public(所有)
static
static修饰的变量内存只有一份,static修饰的变量和方法可以直接通过类名访问。static修饰的代码块只会执行一次
final
final修饰的变量成为常量,final修饰的方法不能被覆盖,final修饰的类不能被继承
super
可以调用父类的方法和属性
数据类型
基本数据类型
整数型(byte,short,int,long)
浮点数型(float,double)单双精度
字符型(char 2)
布尔型(boolean 1)
引用数据类型
类和接口、数组
变量类型
常量(final 修饰,无默认值,存在常量池)
类变量/静态变量(static修饰,有默认值,存在方法区)
成员变量/实例变量(方法外,有默认值,存在堆中)
局部变量(方法内,无默认值,基本数据类型存在栈中,引用数据类型的变量在栈,但指向的对象在堆中)
面向对象
定义
面向过程注重事情的每个步骤及顺序,面向对象注重参与事情的对象有哪些,以及他们各自需要做什么
三大特性
封装
将类的某些信息隐藏在内部(private),对外通过方法调用,不用关心具体实现。减少耦合
继承
从已有的类中派生出新的类,能继承并扩充已有类的属性和方法(父类private不能继承)
多态
父类或接口定义的引用变量可以指向不同子类的实例对象,实现对引用变量的同一个方法调用,执行不同的逻辑。让代码更好的扩展和维护。
特殊类
抽象类(abstract)
“是不是”的概念,1.不能创建对象,只能被继承;2.可以定义抽象方法。
接口(interface)
“有不有”的概念,1. 方法都是抽象的,变量都是final修饰的常量。2. 不能创建对象,只能被类实现或者被接口继承。3. 1.8后新增default方法,不是抽象方法,不用实现。
内部类
作用
1. 内部类可以更方便访问外部类成员(否则只能通过外部类对象访问),2. 每个内部类都能独立的继承一个接口的实现,使多继承变得更加完善.
注意
1. 内部类和外部类可相互访问private属性。2. 非静态的内部类不能定义静态方法和变量。
调用内部类变量或方法
1. 间接调用(通过外部类方法调用)。2. 直接调用(定义内部类对象调用)Outer.Inter inter=new Outer.new Inter();
匿名内部类
new对象时,直接接创建类new X{public void f(){…}}
高阶
泛型
含义
类定义时不设置属性或方法具体类型,实例化的时候再指定具体类型。优点:代码重用、保护类型的安全以及提高性能。
泛型通配符
class A{}:可以定义任意类型
class A{}:只能定义B或B的子类
class A{}:只能定义B或B的父类
反射
具体:反射就是先得到Class类对象,再通过该对象获得它的成员变量/构造方法/成员方法,最后分别通过成员变量/构造方法/成员方法的对象调用对应的方法
作用:可以动态获取类的信息, 提高代码灵活度,框架中比较常见
具体流程:
准备阶段:编译期将每个类的元信息保存在Class类对象中
获取Class类对象:三种方法x.class/x.getClass()/Class.forName()
实际反射操作:根据获取的Class对象,来获取属性,方法,构造方法(Filed/Method/Constructor)
动态代理
作用:类似Spring的aop,可以动态的,不修改源代码的情况下为某个类增加功能,如在一个方法的前后添加一下功能。
异常
Throwable
Error(错误):程序无法处理
栈/内存溢出
虚拟机运行错误
Exception(异常):程序可以处理
RuntimeException及其子类:如下标越界
非RuntimeException异常:可查异常,需要try catch 或throws。如文件上传
IO
字节流:以字节为单位,可处理任意类型数据
字符流:以字符为单位,一次性可读多个字节,处理字符类型数据。
集合
collection(单列集合)
list(可重复,有序:元素存取顺序一样)
查快:ArrayList
底层数组,查快,增删慢,线程不安全,效率高
ArrayList的扩容:使用无参构造方法时,初始大小是0,当有数据插入时,扩展到10。每当容量到达最大量就会自动扩容原来容量的1.5倍(会把老数组元素重新拷贝一份到新数组,代价较高,所以知道初始容量,可以初始化时指定一个初始容量)。
线程安全
Vector:线程安全,结构跟ArrayList类似。内部实现直接使用synchronized 关键字对 每个方法加锁。性能很慢。现在淘汰(synchronizedList跟Vector类似,区别是使用synchronized 关键字对 每个方法内部加锁)
CopyOnWriteArrayList:线程安全,在写的时候加锁(ReentrantLock锁),读的时候不加锁,写操作是在副本进行,所以写入也不会阻塞读取操作,大大提升了读的速度。缺点:写操作是在副本上进行的,数组的复制比较耗时,且有一定的延迟。
删快:LinkedList
底层双向链表(所以可以作为栈和队列),查慢,增删快,线程不安全,效率高
set(唯一,无序:元素存取顺序不同)
未排序:HashSet
底层hashMap(对应value为object常量对象),先判断对象hashcode是否重复,如果不重复那肯定就没添加。如果重复再通过equals比较。
有一个子类LinkedHashSet,底层为链表和哈希表,依赖链表保证有序
排好序:TreeSet
底层红黑树(Compareable保证唯一)
map(双列集合,无序)
未排序:HashMap
线程不安全,底层数组+链表+红黑树(1.8之前为数组+链表)。(扩展:1.8后链表插入从头插法改为了尾插法,因为头插法在多线程中可能导致死循环:在扩容时,头插法打乱了链表的顺序,第一个线程扩容后,顺序相反。此时第二个线程再进行扩容时,就会出现死循环,本来a节点的next时b,单第一个线程扩容后b的next节点是a)
hashMap添加和扩容
put添加过程
1. 根据key获得哈希码,计算数组下标位置。
key通过hashcode获得hash码
位置计算:hash码&(length-1),比  hash值%数组长度  更快
2. 如果对应位置没有元素  就直接把封装好的对象放到该位置。如果对应位置是红黑树节点,就把新节点放到红黑树节点上(期间会判断是否存在key,存在就更新)。如果是链表节点,就通过尾插法插入链表节点,然后判断节点数大于等于8就转为红黑树。
3. 最后判断是可否需要扩容
扩容机制(默认大小16)
先生成新数组(默认原两倍),然后遍历老数组每个元素,计算对应新数组位置。如过某个位置元素大于等于8就转红黑树。
加载因子
默认0.75(hashmap默认大小16,乘以加载因子为12,所以达到12就进行扩容。)加载因子越大,空间利用率越高,但冲突机会增加。
线程安全的
线程安全的HashTable,底层数组+链表,通过Synchronized对整张表加锁保证安全,现已淘汰
线程安全的concurrentHashMap 
1.7是segment数组+hashEntry实现。一个segment中包含一个hashEntry数组,hashEntr是链表结构。线程安全是通过ReentrantLock对Segment数组加锁实现,
1.8后是数组+链表+红黑树实现。线程安全是通过使用了CAS 加 volatile 或者 synchronized 的方式来保证线程安全
添加元素
判断容器是否为空
为空则使用volatile和cas来初始化。(初始化时,通过volatile修饰的变量来表示正在初始化,保证可见性和有序性,cas操作保证原子性)。
如果不为空则根据元素key计算存储位置是否为空
如果空就通过cas操作添加节点元素。(cas操作里还会判断一次是否为空)
如果不为空则通过Synchronized对节点加锁后,添加或更新节点元素。(hash值和key都相同就替换,或者就添加到红黑树或者链表节点中)
获取元素:通过volatile保证获取的元素是最新的。
扩容:扩容也是volatile和cas保证线程安全。扩容过程中通过Synchronized锁住正则迁移的一个 Node 节点减少了加锁粒度。(扩容过程中也可以get 和put)
根据key排好序:TreeMap
底层红黑树,各操作O(logn)
线程
状态
新建状态,new一个线程后
就绪状态,调用start后
运行状态,执行run方法
阻塞状态,如调用sleep,wait等方法后
终止状态,如线程正常结束,或者调用stop等
开启线程三种方式
继承Thread类,重写run方法。(代码:Thread t=new MyThread()😉
实现Runnable接口,在类中实现run()方法(代码:Thread t=new Thread(new MyThreadt())😉
实现Callable接口,实现call方法,再结合FutureTask 创建线程(可获取线程结束后的返回值)(代码:Thread t = new Thread(new FutureTask<>(new MyCallable));)
比较:接口实现优势:1.避免java中的单继承的限制,2. 使用接口的方式能放入线程池。(Callable较Runnable而言,线程可以有返回值)
execute和submit
execute只能提交Runnable类型的任务,无返回值。submit可以提交Runnable和Callable,有返回值
线程相关方法
Thread.sleep(long millis) :线程睡眠,线程转到阻塞状态(不释放锁)
Obj.wait() :线程等待,线程转到阻塞状态(释放对象锁,需要notify唤醒)
Obj.notify(): 线程唤醒,唤醒等待中的线程(注:它不是立即唤醒,notify()是在synchronized语句块内,在synchronized(){}语句块执行结束后当前线程才会释放对象锁,唤醒其他线程)
Thread.yield() :线程让步,让相同优先级的线程之间能适当的轮转执行
join(): 线程加入,在当前线程中调用另一个线程的join()方法,则当前线程转入阻塞状态,直到另一个进程运行结束,当前线程再由阻塞转为就绪状态。
interrupt():线程中断,向其他线程发送一个中断信号,让线程在无限等待时(如死锁时)能抛出异常。
注:线程优先级范围1-10,默认为5。main()本身也是一个线程,当main结束后,子线程仍会继续执行到结束。每个程序至少有两个线程,main和垃圾回收线程(每个程序启动都会启动一个jvm)
线程池
定义
可以容纳多个线程的容器,其中线程可以反复使用,避免反复创建线程的开销(同时新来任务也不用去创建线程比较快)。
创建线程池步骤
创建线程池:ExecutorService service = newFixedThreadPool(5);//(读音 e g z ki ter)   另一种创建方式: ExecutorService service = Executors.newFixedThreadPool(5);//
加入线程:service.submit(new MyCallable());//或者service.submit(new MyRunnable ());加入后会自动调用run方法
关闭线程池:service.shutdown();
线程池的七大参数
常驻核心线程数(corePoolSize):线程中长驻的核心线程
最大线程数(maximumPoolSize):大于等于1
多余的空闲线程存活时间(keepAliveTime):线程数超过常驻核心线程数时,多余线程的空闲时间达到keepAliveTime时就会销毁
unit:keepAliveTime的单位
任务队列(workQueue):被提交但是尚未被执行的任务。当任务达到核心线程数过后,就会放到任务队列中,任务调度时候再取出
线程工厂(threadFactory):用于创建线程
拒绝策略(handler):当工作线程大于等于最大线程数且任务队列也满了时,就会触发拒绝策略(4种:1. 异常策略(AbortPolicy):直接抛出异常。2. 丢弃策略DiscardPolicy:新来任务被提交后直接丢弃。3. 淘汰最久任务策略DiscardOldestPolicy:丢弃存活时间最长的任务。4. 执行策略(CallerRunsPolicy):让提交任务的线程自身去执行该任务)。
线程池执行流程
提交任务
判断核心线程是否满,没满创建线程执行提交任务(就算有空闲线程也会创建,保证创建满核心线程数)
核心线程满判断任务队列是否满,没满放任务队列,等核心线程空闲再去执行。
任务队列也满,判断最大线程数是否满,未满创建线程,满就触发拒绝策略
ThreadLocal
介绍:线程本地存储机制,它可以将数据缓存到某个线程内部,因为共享区的数据为了安全会使用加锁等机制,从而影响效率。
底层:每个线程都有一个ThreadLoacalMap:数据是以key为ThreadLocal对像,值为数据缓存到ThreadLocalMap中的,
场景:比如把用户信息存入Token中,当用户调用接口时,拦截器中解析Token(header中携带 Token),把用户信息存在ThreadLocal中,然后就可以在controller,service等多层方便的共享用户数据,并且能保证对每个请求,都只能访问当前请求的用户信息
注意:因为线程池中的线程不会销毁,所以ThreadLocal对应的值也不会被回收会导致内存泄漏,所以建议在使用完后remove掉。
和加锁对比:加锁用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。
java中的四种线程池
CachedThreadPool-可缓存线程池:线程池中有空闲线程就利用,没有就创建(没有线程数量限制)。
FixedThreadPool-定长线程池:固定线程总数,如果没空闲任务则放到任务队列中(等待线程可以采用先进先出,或者后进先出等唤醒)
SingleThreadExecutor-单线程池:提交的线程按顺序一个个执行,就是一个线程执行完才执行下一个(最大线程数和核心线程数都为1)
ScheduledThreadPool-调度线程池:可以根据设定的时间间隔执行任务
锁(保证线程安全)
Synchronized和ReentranLock
不同点
Synchronized是一个关键词(jvm层面的锁),ReentranLock是一个类(API层面的锁)
Synchronized可以修饰方法和代码块(在方法前加static 可锁住类的所有对象),Jvm自动释放锁。ReentranLock只能修饰代码块。需要手动释放锁
Synchronized锁的是对象,锁信息保存在对象头中的Markword中(记录四个锁状态),ReentrantLock锁的是线程,锁的信息保存在AQS 中
相同点:Synchronized和ReentranLock都是可重入锁
就是在一个线程中允许你反复获得同一把锁,若多次加锁记需要多次解锁才能释放(实现:每个锁关联一个请求计数器和一个获得该锁的线程,加一次锁计数器加1)
为什么需要可重入锁:最大程度避免死锁(对象中加锁的A方法调用加锁的B方法,就会导致死锁,可重入锁允许一个线程反复或的一把锁,就不会导致死锁)
ReentrantLock 相对 synchronized 多了三种功能
1.等待可中断(正在等待的线程可以选择放弃等待,改为处理其他事情。)
2.可实现公平锁(默认非公平)
公平锁:在获取锁时,会先检查AQS同步队列是否有线程在排队,有就进行排队(AQS先进先出的双向队列)
非公平锁:获取锁时不会检查是否有线程在排队,而是直接竞争锁(如果没竞争到锁,后面就跟公平锁一样也会去排队,当锁释放只会唤醒AQS队列的首个线程,非公平只体现在加锁阶段,不体现在唤醒阶段)
3.可绑定多个条件,实现有选择的唤醒等待(synchronized结合notifyAll()会唤醒所有等待的线程。Reentrantlock中一个锁对象可以创建多个对象监视器,线程对象可以注册在指定的对象监视器中,从而可以有选择性的进行线程通知)
Synchronized相关
为什么说Synchronized是一个重量级锁
Synchronized 底层实现依赖于操作系统的互斥锁。而操作系统实现线程之间的切换需要从用户态转换到核心态,转换耗时,成本非常高。这种依赖于操作系统互斥锁的称为重量级锁。
锁机制如何升级
1.6之前就直接是重量级锁,但效率比较低,所以后面引入锁升级机制,用于平衡安全和效率的问题 。
偏向锁:在对象头中记录当前获得该锁的线程id,下次该线程就可以通过偏向锁直接获取到资源。(因为大多数时候是不存在锁竞争的,常常是一个线程多次获得同一个锁,因此如果每次都要竞争锁会增大很多没有必要付出的代价,为了降低获取锁的代价,才引入的偏向锁)
轻量级锁:每个竞争线程通过自旋不停看被锁住资源是否释放。避免用户态到内核态的切换。
重量级锁:由操作系统来判断锁住资源是否释放,释放再通知其他线程。
过程
先尝试用偏向锁的方式去竞争资源。
如果失败就表示其他线程已经占用了偏向锁,此时升级成轻量级锁,通过自旋的方式尝试去加锁。
如过多次竞争失败就会升级成重量级锁(因为太多线程不断自旋竞争对效率影响也比较大),此时没竞争到锁的线程会被阻塞,
双重检查锁DCL(Double Check Lock)
作用:尽可能减少加锁的范围(锁如果加在第一重,初始化除了创建对象可能还涉及其他赋值操作,所以加锁范围太大。在初始化中创建 对象为什么还要二重检查?因为第一重检查未加锁,所以可能导致多个线程都判断对象为空,从而进入初始化方法,依次创建多个对象)
ReentrantLock相关
使用:先创建一个ReentranLock对象,然后通过lock()和unLock()方法加锁和释放

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值