Java高阶知识体系总结(一)

Java高阶知识体系总结

1.Java 基础

Java类设计的原则就是内聚性,一致性和封装性是Java设计的基本原则
1.1 Java基础理论
Java基础理论知识
1.2继承的优缺点
优点
新的实现很容易,因为大部分是继承而来的,很容易修改和扩展已有的实现
缺点
1)打破了封装,因为基类向子类暴露了实现细节 。
2)白盒重用,因为基类的内部细节通常对子类是可见的
3)当父类的实现改变时可能要相应的对子类做出改变 。
4)不能在运行时改变由父类继承来的实现
由此可见,组合比继承具有更大的灵活性和更稳定的结构,一般情况下应该优先考虑组合。

只有当下列条件满足时才考虑使用继承:
1)子类是一种特殊的类型,而不只是父类的一个角色
2)子类的实例不需要变成另一个类的对象
3)子类扩展,而不是覆盖或者使父类的功能失效

2、JVM内存结构

2.1堆栈

栈stack和堆heapJAVA在程序运行时,在内存中划分5片空间进行数据的存储。
分别是:1:寄存器。2:本地方法区。3:方法区。4:栈。5:堆

栈内存:基本数据类型、局部变量都是存放在栈内存中的,用完就消失。
堆内存:new创建的实例化对象及数组,是存放在堆内存中的,用完之后靠垃圾回收机制不定期自动消除。

1).函数中定义的基本类型变量,对象的引用变量都在函数的栈内存中分配。
2).栈内存特点,数数据一执行完毕,变量会立即释放,节约内存空间。
3).栈内存中的数据,没有默认初始化值,需要手动设置。

1).堆内存用来存放new创建的对象和数组。
2).堆内存中所有的实体都有内存地址值。
3).堆内存中的实体是用来封装数据的,这些数据都有默认初始化值。
4).堆内存中的实体不再被指向时,JVM启动垃圾回收机制,自动清除,这也是JAVA优于C++的表现之一
(C++中需要程序员手动清除)

3、Spring

3.1、简介

什么是spring,为什么要用spring?

1.Spring是一个分层的,一站式的轻量级开源框架
2.它致力于提供一种方法管理业务对象,使JavaEE的开发更方便快捷.
3.Spring不是仅仅专注于某一层的开发,而是贯穿应用于web层/业务层及持久层,
与struts2/SpringMVC/hibernate等框架进行无缝整合,
并对一些API(JDBC/JavaMail/远程调用等)进行了封装,降低了这些API的使用难度
4.继承Junit4,方便程序测试,@runwith(SpringJunit…)
5.对支持声明式事物管理@transational,只需通过配置就可以完成对事物的管理,无需动手编程
6.对AOP(Aspect Oriented Programming)编程的支持,Spring提供面向切面编程,方便实现对程序进行权限拦截/运行监控等功能
7.Spring 就是一个大工厂,可以将所有对象创建(IOC)和依赖关系维护(DI),交给Spring管理

3.2、IOC

IOC为InversionOfControl,控制反转是spring基于Java反射技术来实现的。IOC并不是spring或者面向对象的专用术语。
IOC 控制反转,是指对象实例化权利由spring容器来管理
实现原理: 工厂+反射+xml配置文件
底层是通过BeanFactory的子接口ApplicationContext接口的实现类ClassPathXmlApplicaitonContex从配置文件applicationContext.xml文件
中读取要创建的类名,再通过它的方法的getBean(“…”)反射的方式,创建出类,真正意义上达到解耦合的目的.
DI(dependency injection 依赖注入)
DI 依赖注入 在spring创建对象的过程中,对象所依赖的属性通过配置注入对象中。
实现原理:在spring框架负责创建Bean对象时,动态将依赖对象注入到Bean组件。

IoC中最基本的Java技术就是“反射”编程。通俗的说反射就是根据给出的类名(字符串)来生成对象。这种编程方式可以让对象在生成时才决定要生成哪一种对象。反射的应用是很广泛的,像Hibernate、Spring中都是用“反射”做为最基本的技术手段。
  在过去,反射编程方式相对于正常的对象生成方式要慢10几倍,这也许也是当时为什么反射技术没有普遍应用开来的原因。但经SUN改良优化后,反射方式生成对象和通常对象生成方式,速度已经相差不大了(但依然有一倍以上的差距)。
IoC最大的好处是什么?因为把对象生成放在了XML里定义,所以当我们需要换一个实现子类将会变成很简单(一般这样的对象都是实现于某种接口的),只要修改XML就可以了,这样我们甚至可以实现对象的热插拔(有点像USB接口和SCSI硬盘了)。

3.3、AOP

面向切面编程,往往被定义为促使软件系统实现关注点的分离的技术。系统是由许多不同的组件所组成的,每一个组件各负责一块特定功能。除了实现自身核心功能之外,这些组件还经常承担着额外的职责。例如日志、事务管理和安全这样的核心服务经常融入到自身具有核心业务逻辑的组件中去。这些系统服务经常被称为横切关注点,因为它们会跨越系统的多个组件。
3.3.1一般用于日志记录,性能统计,安全控制,事务处理,异常处理等等。
3.3.2可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,提高代码的灵活性和可扩展性,AOP可以说也是这种目标的一种实现。

3.4 spring事务传播特性

事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。
spring支持7种事务传播行为:

propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作

Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。

3.5 spring-boot

spring和springboot区别

项目配置:
Spring: 在Spring中,项目配置通常需要使用XML文件(如applicationContext.xml)或Java配置类来定义Bean、注入依赖等。
Spring Boot: Spring Boot采用约定大于配置的原则,通过默认的配置减少了开发者的工作量。它使用注解和自动配置,不需要繁琐的XML配置,大多数配置可以通过属性文件(如application.properties或application.yml)进行管理。
依赖管理:
Spring: 在Spring中,你需要手动管理项目的各种依赖,包括版本控制。
Spring Boot: Spring Boot使用了“Starter”依赖,这是一组预定义好的依赖集,可以快速集成常用的功能,开发者只需要在项目中引入相关的Starter依赖,而不需要手动配置每个依赖的版本。
内嵌服务器
Spring: 在Spring中,你需要手动配置和集成Web服务器,如Tomcat或Jetty。
Spring Boot: Spring Boot内置了嵌入式的Web服务器(如Tomcat、Jetty或Undertow),默认情况下不需要任何额外的配置,你可以直接运行一个独立的JAR文件来启动应用。
开发体验
Spring: 需要手动配置和管理很多细节,可能显得繁琐。
Spring Boot: 简化了配置和开发流程,使开发者更专注于业务逻辑的实现,提供了更好的开发体验。

4、Java线程

多线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的。
一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
多线程是多任务的一种特别的形式,但多线程使用了更小的资源开销。
Java 提供了三种创建线程的方法:
通过实现 Runnable 接口;
通过继承 Thread 类本身;
通过 Callable 和 Future 创建线程。
有效利用多线程的关键是理解程序是并发执行而不是串行执行的。例如:程序中有两个子系统需要并发执行,这时候就需要利用多线程编程。
通过对多线程的使用,可以编写出非常高效的程序。不过请注意,如果你创建太多的线程,程序执行的效率实际上是降低了,而不是提升了。
请记住,上下文的切换开销也很重要,如果你创建了太多的线程,CPU 花费在上下文的切换的时间将多于执行程序的时间!
同步是多线程中的重要概念。同步的使用可以保证在多线程运行的环境中,程序不会产生设计之外的错误结果。同步的实现方式有两种,同步方法和同步块,这两种方式都要用到synchronized关键字。
eg:

/** * 用同步方法实现 * * @param money */
 public synchronized void save(int money) { 
 		account += money;
 } 
 /** * 用同步代码块实现 * * 
 @param money */ 
	 public void save0(int money) { synchronized (this) {
	 		 account += money;
   		}
    }

使用非依赖属性实现同步

4.1锁

Java中的锁y有 偏向锁、轻量级锁、自旋锁、重量级锁
锁从宏观上分类,分为悲观锁乐观锁
乐观锁:是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,采取在写时先读出当前版本号,然后加锁操作(比较跟上一次的版本号,如果一样则更新),如果失败则要重复读-比较-写的操作。
java中的乐观锁基本都是通过CAS操作实现的,CAS是一种更新的原子操作,比较当前值跟传入值是否一样,一样则更新,否则失败。
悲观锁:是就是悲观思想,即认为写多,遇到并发写的可能性高,每次去拿数据的时候都认为别人会修改,所以每次在读写数据的时候都会上锁,这样别人想读写这个数据就会block直到拿到锁。java中的悲观锁就是Synchronized,AQS框架下的锁则是先尝试cas乐观锁去获取锁,获取不到,才会转换为悲观锁,如RetreenLock。

4.1.2、Java中的锁

自旋锁:自旋锁原理非常简单,如果持有锁的线程能在很短时间内释放锁资源,那么那些等待竞争锁的线程就不需要做内核态和用户态之间的切换进入阻塞挂起状态,它们只需要等一等(自旋),等持有锁的线程释放锁后即可立即获取锁,这样就避免用户线程和内核的切换的消耗。
但是线程自旋是需要消耗cup的,说白了就是让cup在做无用功,如果一直获取不到锁,那线程也不能一直占用cup自旋做无用功,所以需要设定一个自旋等待的最大时间。
如果持有锁的线程执行的时间超过自旋等待的最大时间扔没有释放锁,就会导致其它争用锁的线程在最大等待时间内还是获取不到锁,这时争用线程会停止自旋进入阻塞状态。
偏向锁(Biased Locking)是Java6引入的一项多线程优化。
偏向锁,顾名思义,它会偏向于第一个访问锁的线程,如果在运行过程中,同步锁只有一个线程访问,不存在多线程争用的情况,则线程是不需要触发同步的,这种情况下,就会给线程加一个偏向锁。
如果在运行过程中,遇到了其他线程抢占锁,则持有偏向锁的线程会被挂起,JVM会消除它身上的偏向锁,将锁恢复到标准的轻量级锁。
轻量级锁:轻量级锁是由偏向所升级来的,偏向锁运行在一个线程进入同步块的情况下,当第二个线程加入锁争用的时候,偏向锁就会升级为轻量级锁;
重量级锁:synchronized会导致争用不到锁的线程进入阻塞状态,所以说它是java语言中一个重量级的同步操纵,被称为重量级锁,为了缓解上述性能问题,JVM从1.5开始,引入了轻量锁与偏向锁,默认启用了自旋锁,他们都属于乐观锁。

4.1.3、锁优化

减少锁的时间:不需要同步执行的代码,能不放在同步快里面执行就不要放在同步快内,可以让锁尽快释放;
减少锁的粒度:将物理上的一个锁,拆成逻辑上的多个锁,增加并行度,从而降低锁竞争。它的思想也是用空间来换时间;
锁粗化:大部分情况下我们是要让锁的粒度最小化,锁的粗化则是要增大锁的粒度;
在以下场景下需要粗化锁的粒度:
假如有一个循环,循环内的操作需要加锁,我们应该把锁放到循环外面,否则每次进出循环,都进出一次临界区,效率是非常差的;
使用读写锁:ReentrantReadWriteLock 是一个读写锁,读操作加读锁,可以并发读,写操作使用写锁,只能单线程写;
读写分离:CopyOnWriteArrayList 、CopyOnWriteArraySet
CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。
 CopyOnWrite并发容器用于读多写少的并发场景,因为,读的时候没有锁,但是对其进行更改的时候是会加锁的,否则会导致多个线程同时复制出多个副本,各自修改各自的;
使用CAS:如果需要同步的操作执行速度非常快,并且线程竞争并不激烈,这时候使用cas效率会更高,因为加锁会导致线程的上下文切换,如果上下文切换的耗时比同步操作本身更耗时,且线程对资源的竞争不激烈,使用volatiled+cas操作会是非常高效的选择;
消除缓存行的伪共享:除了我们在代码中使用的同步锁和jvm自己内置的同步锁外,还有一种隐藏的锁就是缓存行,它也被称为性能杀手。
在多核cup的处理器中,每个cup都有自己独占的一级缓存、二级缓存,甚至还有一个共享的三级缓存,为了提高性能,cpu读写数据是以缓存行为最小单元读写的;32位的cpu缓存行为32字节,64位cup的缓存行为64字节,这就导致了一些问题。

4.1.3、死锁

死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。
java 死锁产生的四个必要条件:
1、互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用
2、不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
3、请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
4、循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。
解决方式:对于无法成功获取的情况,一般就是重复尝试,或指定尝试的次数,也可以马上退出。

4.2、Threadlocal

ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度。
ThreadLocal在每个线程中对该变量会创建一个副本,即每个线程内部都会有一个该变量,且在线程内部任何地方都可以使用,线程之间互不影响,这样一来就不存在线程安全问题,也不会严重影响程序执行性能。
但是要注意,虽然ThreadLocal能够解决上面说的问题,但是由于在每个线程中都创建了副本,所以要考虑它对资源的消耗,比如内存的占用会比不使用ThreadLocal要大。

1)实际的通过ThreadLocal创建的副本是存储在每个线程自己的threadLocals中的;
2)为何threadLocals的类型ThreadLocalMap的键值为ThreadLocal对象,因为每个线程中可有多个threadLocal变量;
3)在进行get之前,必须先set,否则会报空指针异常;

4.3、Java对象的引用

包括:强引用,软引用,弱引用

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值