Skip to content

介绍一下?

Java 🚀

Java是一种面向对象的开发语言,源代码编译成字节码,通过JVM解释执行,具有跨平台、高性能、自动垃圾回收等特点。特别是其强大Spring生态,可以方便得构建高可用、高并发、高性能的企业级应用。阿里、京东等互联网巨头的后台服务,大多采用就通过Java技术栈实现。总之,Java是一个优秀的开发语言,广泛应用于Web编程,具有很好的发展前景。

Java集合 🚀

Java常用的集合有List、Set、Map,这些都是接口,具体的实现类有ArrayList LinkedList HashSet HashMap等,不同的实现类有不同的特性。比如ArrayList基于数组实现,具有随机访问快的特点,适用场景比较多。LinkedList基于链表实现,增加元素、删除元素不用移动元素,增删速度快。HashSetHashMap基于散列表技术,HashSet可以用于去重,HashMap可根据key快速查询对应的value,且时间复杂度为O(1)。刚才说的这些,都是单线程模式下使用的。在多线程场景下,为了保证高并发时逻辑处理的正确性,可以使用CopyOnWriteArrayListCopyOnWriteArraySetConcurrentHashMapArrayBlockingQueueLinkedBlockingQueueLinkedBlockingDeque等线程安全的集合类,这些类已经封装在了java.util.concurrent包中,使用非常方便。

Java多线程 🚀

多线程就是多个线程分时利用CPU等系统资源,让每个线程都得到执行的机会,整体来看好像多个任务在同时运行。多线程有很多好处,比如可以多个任务同步进行,比如看电影的时候视频与音频同步播放。再比如在I/O操作密集的场景,使用虚拟线程可以自动挂起正在等待IO的线程并切换其他线程,这样以来减少CPU等待时间,提升了系统整体性能。线程用Thread类表示,可以通过继承Thread或者实现Runnable、Callable接口创建Thread类并通过start()方法启动线程。线程有6种状态,created terminated runnable wait time_wait block,四种中间态可以切换。线程操作常用的方法有wait(), notify(), notifyAll(), sleep(), interrupt(), isInterrupted()等,可以挂起、唤醒、休眠、中断线程等。

多线程开发中,为了保证线程安全,除了自己手写线程安全逻辑代码,也可以使用java.util.concurrent包的封装好的类与方法,比如可重入锁ReentrantLock、代替wait/notify的Condition、读写锁ReadWriteLock、乐观锁StampedLock、限制线程并发量的Semaphore [ˈseməfɔːr]、原子操作的Atomic包、线程安全的Concurrent集合比如CopyOnWriteArrayListCopyOnWriteArraySetConcurrentHashMapArrayBlockingQueueLinkedBlockingQueueLinkedBlockingDeque等。

MVC模式 🚀

MVC是一种软件分层的思想,M即Model是模型,V是View即视图,C是Controller控制。MVC思想可以把控制逻辑、业务处理逻辑、显示逻辑分开处理,使软件架构更清晰。最开始工作的时候接触的是SpringMVC这个框架,请求一个网页基本的流程是这样的:请求首先被前置的DispatchServlet处理并分发给路径匹配的Controller处理,Controller调用Service层进行逻辑处理并返回ModelAndView对象,之后通过JSP/Theamleaf等框架渲染处理并返回生成好的HTML给用户浏览器。总之,MVC在前后端不分离的项目中,使通过分层使用处理逻辑更清晰,开发更高效。

SSM 🚀

SSMSpringSpringMVCMyBatis的组合,是JavaEE领域继SSH之后,一个经典的开发框架组合。Spring提供了IOC AOP等基础功能,SpringMVC负责分发用户请求,MyBatis映射了SQL语句并封装了JDBC的操作,三者结合,可以快速得开发一个典型的JavaEE应用。

Spring 🚀

Spring是一个JavaEE开发的框架,开放包容,不仅自己优秀,还可以整合MyBatisRedission等优秀的第三方框架。Spring的核心是IOCAOP,即控制反转和面向切面。控制反转,即把对象的管理交给IOC容器,凡是被@Controller @Service @Component @Configuration等注解的类将会在容器中自动创建实例,在需要使用的地方直接通过@Autowired注入即可使用。AOP即面向切面编程,Spring通过AspectJ实现了动态代理,通过AOP可以增强方法功能,简化代码逻辑。比如通过AOP功能,我们一个@RequestMapping注解,就让一个方法拥有了http请求的服务能力,帮助我们封装了请求的解析与响应的处理,我们只需要关注业务逻辑即可。再比如整合MyBatis后的Spring框架,一个@Mapper注解就可以让一个方法拥有数据库操作的能力,帮助我们封装了数据库的连接与处理结果的解析等。总之,Spring的出现让JavaEE的开发更加方便,功能更加强大。

静态代理与动态代理 🚀

代理是一种结构型的设计模式,根据代理类生成的阶段,一般可分为静态代理与动态代理。静态代理在编译期代理类就生成了,运行时代理类已经有了,动态代理在代码运行时才产生。动态代理相比静态代理,灵活性更好,但因为要代理类在运行期产生,所以性能上会有一些影响。代理模式在开发中很常见,SpringAOP就是通过动态代理实现的,对有接口的使用JDK动态代理,对普通方法使用CGLib动态代理。通过代理类可以增强我们代码的功能,比如一个@Transanctional让一个普通的方法支持了事务,一个@Service注解配合AOP就可以实例化一个类并放到IOC容器进行管理 ,通过一个@Controller一个@RequestMapping就可以让一个普通的方法处理用户浏览器发来的HTTP请求。在实际编码中,一般用Spring AOP功能多一些,创建一个切面并通过注解指定切点,可以用来实现统一打印参数日志、计算方法运行时间等功能。Spring AOP是运行时织入,AspectJ AOP可以在编译时、编译后、加载前织入,功能更强大,执行效率高,但编译还需要acj,相对复杂。

Spring事务传播机制 🚀

Spring事务中的传播通过@Transanctionalpropagation属性来定义,默认的是required,即当前如果存在事务就加入当前事务,如果不存在就创建新的事务。除了required之外,还有required_newsupportednot_supportedmandatorynevernested(套嵌),分别定义了不同的事务传播策略。比如required_new会创建新的事务,如果当前存在事务则会把当前事务挂起。supported表示,如果当前有事务就加入当前事务,当前没有事务就以非事务方式执行。not_supported表示以非事务方式执行,如果当前存在事务就把当前事务挂起。mandatory表示,使用当前事务,如果当前没有事务就抛出异常。never表示以非事务方式执行,如果当前存在事务就抛出异常。nested表示套嵌事务,此方法在套嵌事务内执行。最常用的还是required,一般使用前会在官方的API文档里去查询,并且写一段代码实际测试一下,保证逻辑操作的准确度。

SpringBoot 🚀

SpringBoot的出现,简化了Spring框架的开发,可以让我们用很少的配置,快速搭建Spring的骨架。相比Spring,它的方便之处在于:

  • 提供了一系列starter,通过起步依赖和自动配置,实现开箱即用。比如我们只需要引入spring-boot-starter-web,就自动引入了SpringMVC Tomcat Jackson等依赖,并且有了默认配置。如果需要改个别参数,比如Tomcat启动端口号,在application.yml里直接加server.port配置就可以生效。

  • 另外,内嵌的Servlet容器如Tomcat/Netty,使得项目可以直接打成jar包通过java -jar运行,不用再打warcopy到服务器目录,部署更方便。

自定义spring-boot-starter 🚀

spring-boot-starter通过起步依赖和自动装配,简化了Spring应用的开发。自定义starter一般有这些步骤,首先得创建一个maven工程并引入一些必要的依赖,其次要写一属性类并加@configuration注解把配置文件里的参数导入进来,然后要写业务类和自动配置类,最后配置META-INF下的spring.factories。这此做好之后,通过maven install安装到仓库里,就可以在其他项目通过pom.xml引入了。想修改相关属性,就改项目的application.yml进行配置。这就是自定义spring-boot-starter的大致流程。

SpringCloud

SpringCloud的是一个微服务框架,

SpringSecurity

待补充。

Redis

待补充。

MongoDB

待补充。

MySQL

待补充。

数据库事务与隔离级别 🚀

事务是将多条SQL打包为一个整体执行,要么全部成功,要不全部失败,不允许部分成功部分失败,破坏数据的一致性。事务的特性总结起来有4个,分别是ACID,即原子性、一致性、隔离性、持久性。原子性指所有SQL执行只能全部成功或者全部失败,不允许部分成功。一致性是指事务执行前后,必须是从一个一致状态转移到另一个一致的状态,举一个经典例子就是转账前后一个账户少了钱另一个账户多了钱且钱的总数不能凭空增加或者减少,I就是隔离性,表示事务之间互相隔离不影响彼此,隔离性是根据事务设定的隔离级别来确定的,一会儿再详细说。最后一个特性就是持久性,一但事务提交,对数据库数据的更改就是永久的,即使宕机导致更新还没被写入数据库,故障修复开机后更新的数据也会及时写入数据库,这块主要是通过redo log来实现。

刚才有提到数据库的隔离级别,定义了不同事务之间互相影响的程度,分别为Read UncommittedRead CommittedRepeatable ReadSerializable,即读未提交、读已提交、可重复读与串行化。

  • 读未提交,是最低的隔离级别,可以读到其他事务未提交的数据,会出现脏读、不可重复读、幻读的情况。
  • 读已经提交,是Oracle数据库默认的隔离级别,可以读取到其他事务提交的数据,不能读未提交的数据,因此可避免脏读,但依然会出现不可重复读、幻读的问题。
  • 可重复读,是MySQL默认的事务隔离级别,通过MVCC多版本控制,让一个事务只可以读取到当前事务开始前数据版本,这样保证了事务中每次读取的数据都是一致的,即可重复读。
  • 串行化是最高的隔离级别,不会出现其他隔离级别会出现的问题,但因为是串行,执行效率低,一般不会去使用。

我们之前项目开发里,一般用的是MySQL默认的事务隔离级别,即Repeatable Read可重复读。

RabbitMQ

待补充。

Linux 🚀

Linux是一个开源的、高效的、高稳定性的操作系统,广泛得应用于服务器领域。工作这么多年,我个人对Linux还是相对比较熟悉的。创建个VM、装的操作系统、挂载个磁盘、配置一下网络、装一些基础软件、用docker部署一个服务这些都还是可以的。我平常用Ubuntu1804EulerOs2.5多一些,常用命令的话安装软件Ubuntuapt install/apt removeEulerOsyum install/yum remove,解压用tar -xzvf ,压缩用tar -czvfdocker拉镜像docker pull,启动docker run,进入容器内docker exec -it 容器名/bin/sh,删除镜像docker rmi,删除容器docker rm -f 容器名称nginx 重新加载配置nginx -s reload。命令还有很多,一般记不清楚就上百度查一下,虽然没有专业运维的人专业,但大多常见的问题还是可以解决的。

算法

待补充。

设计模式

待补充。

JVM

待补充。

MyBatis

待补充。

面向对象 🚀

面向对象是一种编程思想,相比面向过程而言有很多优势。面向过程的时候我们关心每个步骤如何处理,面向对象只需要考虑什么对象有什么功能,不必关心对象内部的具体实现,可以简化代码编写难度、降低代码耦合度、增加代码复用率等。面向对象有三大特性,分别是封装、继承与多态,这些我特性体现出了面向对象的优势。封装,可以把属性与方法整合到一个类中,隐藏实现细节,提供公开方法供外部调用。通过封装,要实现某个功能,直接调用公开的方法即可,开发难度大大降低。继承,就是子类自动拥有父类的方法,继承之后还可以重写父类方法或者扩展新的方法,实现自己的功能。通过继承,可以减少自己重新造轮子,增加代码复用率的同时提升了开发效率。多态,从现象上来讲就是父类引用指向子类对象,不同的子类对象可以有不同的实现。通过多态,我们可以面向接口编程,或者叫面向抽象编程。这样的话,实现类更新时,不需要更新上层代码就可以兼容,代码维护难度大大降低。比如JDBC就是抽象接口的组合,我们面向JDBC编程时,如果底层数据库是Oracle我们就导入Oracle的驱动,底层是MySQL我们就导入MySQL的驱动,我们的业务代码无需修改。总之,封装、继承、多态三大特性解耦了代码,简化了代码开发、维护难度,让面向对象成为优秀的编程方式。

异常处理 🚀

异常处理,就是定义程序在执行中出现异常的时候应该走的处理流程,可以让我们的代码更加强壮。JavaThrowable是所有异常类的父类,在它之下分为Error和Exception,Exception又分为RuntimeExceptionCheckedExceptionError是指程序无法处理的错误,比如内存溢出OutOfMemoryOOM)、堆栈溢出StackOverFlow等。CheckedException是受检异常,必须在代码里处理,否则编译通不过。RuntimeException是运行时异常,比如空指针异常、除0异常等,为了增加代码强壮性,代码里也应该处理,即使编译时不会提示。我们在进行异常处理的时候,要注意保留原始异常并打印日志堆栈,方便定位问题,不能只在catch中接收异常但不处理,这样代码问题将难以定位。另外,也要注意信息安全,不能把敏感的异常信息返回到系统外部,这样有安全隐患。总之,Java异常处理让我们的程序更加健壮,也可以让我们将异常处理代码与业务代码分离,有很多好处,我们需要注意异常代码的编写。

反射 🚀

反射是指程序在运行期,可以通过Class对象拿到这个类的所有信息,从而可以读写类或对象的所有字段、调用所有方法、甚至可以动态创建实例。这些操作都是非常规的,编码繁琐,平时开发直接使用得用少。更多情况是给工具或者底层框架来使用,解决在运行期对某个实例一无所知的情况下,如何调用其方法,获取特定字段的值,创建类的实例。动态代理就用到了反射,可以在运行期间,通过Proxy生成代理对象,达到方法增强的目的。

泛型 🚀

泛型是一种“代码模板”,可以用一套代码套用各种类型。有了泛型,我们就不用针对每种数据类型定义操作类了,比如ArrayList使用泛型后,可以给各种类型如String Integer 等使用,实现了一套代码,多种类型复用。Java中的泛型通过擦除法实现,也会有一些局限性,比如无法获取泛型类的Class,返回的总是Object,原因是内部存储使用的都是Object,会在编译期进行检查与强制转换。因为泛型类都是Object,所以使用了泛型不能使用基本类型、也不能进行实例化。如果一定要获取泛型的Class对象,可以通过构造方法参数传入,然后在泛型类中就可以取得Class对象操作了。

单元测试

待补充。

Web开发

待补充。

自已

待补充。

之前的工作

待补充。