基础知识

编译时与运行时

Java编译器会对源代码进行相应的优化,如下面这个例子:
曾经的学习笔记 - 图1
经过反编译后源代码为:
曾经的学习笔记 - 图2
其中,计算product1时就是编译时做的优化——由于已知number1和number2,并且皆为final,那么它们相乘的结果也就确定了;

除此之外,方法的重载、泛型编程也是在编译时确定的,因为在编译的时候根据传入的参数列表等信息就可以推断出所选用的函数及实际的模板类型(将范型类型重写成真实类型的过程,被称为类型擦除);

而方法的重写,即子类对父类方法的重写,就只能在运行时由程序确定,因为只有在运行到目标代码段时,才知道到底应该选择什么类的执行函数。

有关“编译时继承”与“运行时继承”?
——编译时继承就是常规情况下我们所说的继承关系;
——运行时继承一般得通过一些方式自己实现,例如使用组合模式、代理模式来实现。

核心类

Object
在Java中的所有类都是从Object类继承出来的,这也是实现万用类(如ArrayList)的基础。
诶?话说,Object类(我们不妨称之为 终极类,其对象称为终极对象)除了实现万用类,还有什么实质性的东西么?比如——终极对象内部存储了什么东西?

——根据使用频度等方面原因,终极对象中主要有:

  • equals():判断两个对象是否相等;
  • getClass():获取当前对象的类;
  • hashCode():该对象的的哈希码,全局唯一标识码;
  • toString():获取类的名称及标识;

除了这些通用方法之外,还为线程同步所服务(见后)。

Object类是抽象的么?
——应该不是,如果是抽象类,那么所有类都必须实现覆盖其中的方法,但是实际情况来看,并没有。

StringBuilder

如果直接使用“+”进行字符串的拼接,每一次拼接字符串都会扔掉旧的对象,新建一个新的对象,效率太低。
针对这个问题,java设计类Stringbuilder类,用于高效拼接字符串;

那么它的内部是如何实现的呢?
分析一波:
在java中,字符串是以字符数组来实现的,一个字符对象,其实就类似C语言中的指针,指向某一个字符数组。
当进行字符串的拼接时,其实是另外新开辟了一块内存空间,将旧的字符串按照顺序拷贝过去。(字符串常量和字符串变量是两个概念,如果是两个常量相加,那么编译器会进行优化的哈)

怎么简化这个步骤?
那就是不重新开辟空间,直接在某一个字符串后续进行添加,当空间不够用时才会进行再次创建,如此就减少了反复创建对象、回收对象所造成的延时——跟C++的Vector很像
参考链接:https://www.cnblogs.com/yangming1996/p/6927898.html

StringBuilder与StringBuffer的唯一区别在于后者是线程安全的,也就是加了锁。
参考:https://www.cnblogs.com/dolphin0520/p/3778589.html
image.png
StringJoiner
这个是为了用于指定分隔符来凭借字符串数组;

垃圾回收

在java体系中,内存分为堆栈。堆,主要是对象的生存空间;栈,主要是方法调用和变量的生存空间。所谓的垃圾回收,都是基于堆而言的。
对于一个引用对象的局部变量而言,局部变量本身在栈上,其引用对象在堆上。

一般对象的创建过程有三个步骤:
声明、创建、赋值,如下:
Duck duck = new Duck();
Duck duck 即是声明;
new * 即是创建;
= 即是赋值;

对象什么时候死?
只要有活着的引用,对象就不会死!
垃圾回收器(GC)会检测活着的引用及相应的对象,如果某个对象的引用都死了,那么这个对象····也就该死了!但是如果每一次都逐一检查程序中的所有对象,那么会比较耗时,因此Java的垃圾回收机制采用一种分代的理念,即新生代、老年代与永生代,各个对象从三个时代逐一迈向后一时代。在进行垃圾回收时,只需要删除老年代即可。参考阅读:https://www.cnblogs.com/rxysg/p/15691759.html
image.png

注解

什么是注解?
放在java源码的类、方法、字段、参数前的特殊“注释”。
注释是给人看的,会被编译器直接忽略掉;注解是给程序看的,会被编译器打包进入class文件

它的主要作用是用作参数的配置,下面是一些常见的注解(省略@):

  • configuration:表明当前类是一个配置类,是方法bean的源;
  • Resource:一般用在树形或set方法上用于为对象参数赋值;
  • Bean:用于告诉方法,产生一个Bean对象,而后交给Spring管理;
  • Component:将这个类交给Spring管理;
  • Override:表示子类重写了父类的方法,可读性提高,编译器检查;

PS:一些java注解是为了代替xml配置;
还有lombok(是一个java的工具类)提供的注解:

  • NonNull:用于检查对象非空;
  • Data;
  • Log

其他详见:https://blog.csdn.net/sunsfan/article/details/53542374
https://my.oschina.net/u/2935389/blog/1036598

多线程

java中建立线程是通过:Thread库。主要有以下三种方式:
一是通过继承Thread,覆盖run()方法;二是实现Runable接口;三是利用lambda语法;

值得注意的是,新版java不存在强制中断线程的方法,但可以通过对目标线程调用interrupt()方法发送中断请求,在目标线程内部执行相应的逻辑来进行线程中断。

MVC

MVC可不是数据库的多版本控制啊,而是Model-View-Controller,即模型-视图-控制器。Model用来实现复杂的业务逻辑,View通过渲染来实现视图的展示。

序列化(serialize)

java提供一种对象序列化的机制:可以将对象表示为一个字节序列(包括该对象的数据、有关对象的类型信息等)。
将上述字节序列可以进行反序列化,在内存中新建对象。

如何让类支持这样的机制?
——该类实现java.io.serializable接口(对应字段上加上transizent可以让该类不序列化),并且该类的所有属性必须是可以序列化的。

工具框架

JFrame&Swing

Java的GUI控件,Swing支持的组件和功能更强大。
三大布局管理器(Layout Managers):
BorderLayout:即分区
曾经的学习笔记 - 图5
FlowLayout:即从左到右,从上到下
曾经的学习笔记 - 图6
BoxLayout:从上到下
曾经的学习笔记 - 图7

Maven

Maven是一个java项目管理和构建工具,它主要是提供来一套标准化的构建项目的流程,并且自动处理依赖管理(比如A依赖于B,B依赖于C,那么A会自动依赖于C)。一个默认的Maven项目结构如下:
曾经的学习笔记 - 图8
依赖关系一共有四种,在scope中定义:
compile表示编译时需要;test表示测试时使用;runtime表示编译时不需要,但运行时需要;provided表示编译时需要,但运行时由JDK或者服务器提供;

PS:那么Maven如何知道在哪去下载依赖?——一个中央仓库,去该中央仓库查询下载链接即可;

Lifecycle生命周期,Maven的生命周期主要有三个常用的生命周期:clean——清理项目、default——构建项目、site生成项目站点。
每一个生命周期都由一系列阶段(Phase)构成,以内置的生命周期default为例,它包含以下这些phase:
曾经的学习笔记 - 图9

clean包含以下的phase:

  • validate
  • compile

运行mvn + XXX,即默认到对应的phase例如mvn packge,会一直执行default生命周期直到到package这个阶段
clean compile test package(打包)。

执行phase会触发goal,goal是啥?没什么特别的,就是具体的操作,见下:
曾经的学习笔记 - 图10
其实:
lifecycle相当于javaSE的package,是一个大整体,包含很多的phase;
phase相当于JavaSE的class,是整体中的一个部分,包含很多的goal;
goal相当于JavaSE的method,是每一个部分的真正操作;
PS:构建项目就是执行lifecycle,执行到制定的phase为止,大多数时候我们不需要考虑goal这一层的东西,例外可以参考mvn 启动Tomcat goal

对于大型项目,需要进行模块化管理,每一个小项目都是一个小的Maven项目,例如三个模块的项目:
曾经的学习笔记 - 图11
如果ABC的pom.xml中存在相同的配置文件,可以单独将这部分配置文件新建为一个xml,而后在ABC中引入即可;

JSP

Servlet是java标准库中的网络连接库。它可以处理HTTP请求,发送HTTP响应。可以通过PrintWriter来发送响应,然后输出HTML,但是输出太麻烦,JSP就是用来简便这个操作的,一个简单的JSP(Java Server Pages)如下:

  1. <html>
  2. <head>
  3. <title>Hello World - JSP</title>
  4. </head>
  5. <body>
  6. <%-- JSP Comment --%>
  7. <h1>Hello World!</h1>
  8. <p>
  9. <%
  10. out.println("Your IP address is ");
  11. %>
  12. <span style="color:red">
  13. <%= request.getRemoteAddr() %>
  14. </span>
  15. </p>
  16. </body>
  17. </html>

整个JSP的内容实际上是一个HTML,但是稍有不同:

  • 包含在<%—和—%>之间的是JSP的注释,它们会被完全忽略;
  • 包含在<%和%>之间的是Java代码,可以编写任意Java代码;
  • 如果使用<%= xxx %>则可以快捷输出一个变量的值

JSP可以简单看作HTML与Java的混合。

框架

什么是框架?
简单来说就是java项目开发的半成品(在此基础上进行自身项目的开发),由一系列的类和接口构成;
现阶段需要了解的框架?
Spring boot, Pandora boot;

Filter

在实际的Web应用程序中,通常有URL映射,也会有多个Servlet来处理URL。以下列为例:
曾经的学习笔记 - 图12
其中,Profile和Post以及Reply三个都需要用户登陆才能操作,否则就跳转到登陆页面,如果将登陆判断都放回到这三个里,代码过于重复,可以利用filter来进行预处理,即将“/user/*”之外的所有请求都屏蔽。
PS:其实就相当于进行了一个过滤(正则匹配之类的)。

JavaBean

当class的定义满足以下规范
若干private实例字段(即成员量);
通过public方法来读写实例字段;
并且读写方法符合以下命名规范(可以只有读/写):

  1. // 读方法:
  2. public Type getXyz()
  3. // 写方法:
  4. public void setXyz(Type value)

那就称为JavaBean。
显而易见,这种结构的主要作用是为了传递数据。

Spring

优良特性

IoC容器

inversion of control,即控制权反转,又称为依赖注入(Dependency Injection)。IoC将组件的创建+配置与组件的使用相分离(例如,A类中需要B类对象,A B中都需要一个Data对象,传统方式是需要A B类中都new 一个Data对象,但是IoC可以只需要一个,实现对象的共享、创建很方便)。IoC容器中创建的组件被称为JavaBean组件。
PS:JavaBean容器是通过读取XML文件使用反射(通过字符串来表示类)机制完成的。

什么是容器?
容器是一种为某种特定组件的运行提供必要支持的一个软件环境。例如,Tomcat就是一个Servlet容器,它可以为Servlet的运行提供运行环境。类似Docker这样的软件也是一个容器,它提供了必要的Linux环境以便运行特定的Linux进程。
所以,Spring的核心就是提供一个IoC容器,可以管理所有轻量级的JavaBean组件,提供包括生命周期管理、配置等底层服务。
PS:BeanFactory和ApplicationContext的区别在于,BeanFactory是按需创建,即第一次获取Bean才创建,但是后者会一次性创建所有的Bean(也就是所谓的饥汉子)。由于后者是继承Beanfactory接口,提供更多的功能。
PPS:注解(Annotation)是另一种配置Bean的方式,相比于XML更加便捷(便于更新扩展)。详见:https://www.liaoxuefeng.com/wiki/1252599548343744/1282382596407330

AOP

Aspect Oriented Programming,面向切面编程。跟OOP(面向对象,主要特点是封装、继承、多态)不同的是,AOP将系统分解为不同的关注点(切面)。
为什么会想到使用切面的方式?在实际的项目开发中,有许多的重复的代码段:例如安全检查、日志、事务等代码,为了避免重复编写代码,可以使用Proxy模式(即将这部分代码放入Proxy中),但是这种方式需要抽取接口并对每个方法进行Proxy的实现。AOP就是为了减少重复的操作次数。

DI

Dependency injection,依赖注入,不需要手动调用setXX方法去设置,而是通过配置赋值;(依赖于控制反转)

容器

Spring是一个容器,它包含并且管理应用对象的声明周期。

组件化一站式

组件化是指可以使用XML或者注解来组合对象;
一站式是指可以整合各种开源框架和第三方类库;

其他

Spring Boot

Spring Boot是一个基于Spring的套件,它帮我们预组装了Spring的一系列组件,以便以尽可能少的代码的配置来开发基于Spring的Java应用程序,可以简单的理解为模块化的Spring。
PS:高度工具化模块化,建议先学会了Spring在进行SpringBoot的开发,不然很容易出现题但是找不到解决的方法

实战教程

参考教程:
https://juejin.cn/post/6844903779519725575
PS:需要新建Spring Boot时,需要安装Spring Assistant,自行百度;
PS: 在Sprigboot项目中,程序启动入口Application只会扫描当前的文件路径下的java代码;

四、其他

class字节码(必备)

这里所说的class文件是什么?
也就是java源代码编译成的字节码。字节码的好处,只需要编译一次,就适用于所有Java支持的平台,因为字节码是全JVM通用的
JVM定义的数据类型包括原始类型(数字类型、boolean类型、returnAddress类型)以及参考类型(类类型、数组类型、接口类型)。
每一个线程都会又一个堆栈(这里就是指栈)来存储局部变量、方法参数和返回值等等;
而堆是所有线程共享的内存,用来存储对象和释放对象(垃圾回收器);
方法区域:对于每个加载的类,它存储方法代码和符号表(例如对字段或方法的引用)以及称为常量池的常量。
更详细的链接以及实际场景的字节码解析参考(强烈推荐!):
.class详解

classloader

用来加载class,负责将calss的字节码形式(可以来自磁盘文件也可以是jar包里的class,也可以是远程的字节流)转换为内存形式的class对象,其实字节码的本质就是字节数组,拥有特定的复杂的内部格式。
PS:许多字节码加密就是通过定制Classloader来实现的,即对字节码进行加密,再用特定的工具来解密。
JVM中内置三个Classloader:

  • BootstrapClassloader:负责加载JVM运行时核心类,如java.util.*等,是用C代码实现的,称为根加载器;
  • ExtensionClassLoader:负责加载JVM扩展类。例如swing等;
  • AppClassLoader:面向用户的加载器,例如classpath中定义的路径

    接口中的default关键字

    接口通常只能声明方法,java8之后可以使用default,表示接口中该方法为普通方法,可以写方法体;这是为了解决“对于已经发布的版本,很难在给接口添加新方法的同时,不影响已有的实现,一切都是为了兼容性”。

    一些问题

    廖雪峰的Java教程
    jdk各版本区别
    maven生命周期

类优先于接口
https://blog.csdn.net/qq_35835624/article/details/80196932
Java TDO
https://www.cnblogs.com/xt0810/p/3654574.html
设计模式的学习——常见的设计模式
设计模式的相关资料请直接查看设计模式笔记。