Java编程思想第4版学习笔记(一)
第二章 一切都是对象(Hello World)
 
       这个笔记本主要记录了我在学习Java编程思想(第4版,中文版)的过程中遇到的重难点及其分析。主要参考了C++11版本的C++语言,对比了它们不同的部分。
 
      知识点0:第一章概括
       在探讨第二章的内容之前,先简要地概括一下第一章说了什么,第一章的标题叫做“对象导论”,第二章叫做“一切都是对象”,这种令人混淆的说法或许让人乍一看很难区分第一二章讲的内容有什么区别。
       不过显然,这两章主要讲述的内容是不同的——第一章主要讲了面向对象编程中一些重要的概念,这些概念在任何一门适合配合面向对象编程的思想进行开发的语言上我们都能看到——就是 对象的概念、接口(类方法)的概念、用对象组合程序的概念、访问控制的概念、继承和派生的概念、多态的概念、容器的概念 和 泛型的概念。
       第一章里提到了一些Java和这些概念的关联和几个细节,比如设计类的内聚性和耦合性、派生类对基类方法的覆盖、编译时的前期绑定和后期绑定、Java的一切基于Object单根继承结构内存分配并发网络编程等概念。第一章涵盖了这些也许要把整本书阅读完再回过头看才能明白一些的基础和复杂的概念,它们并不完整,有很多点都需要在实践中不断的补充完善。因此也许初学者不需要一开始纠结于弄明白第一章,而是要在学会C/C++之类的语言的一些基础后,起码编过程序,再来学习这本书,粗读第一章,然后从第二章开始。
        第二章主要讲了Java中对象的表现形式——引用、基本类型和其包装器类型、简单的对象生命周期/作用域、创建类类型、类字段(数据成员)和方法、初始化、类内函数、包、静态成员、HelloWorld、编译运行、注释和Javadoc。(这里面提到的概念比第一章少多了而且更具体),总之,第二章就是一个HelloWorld式的章节,浅显的讲了一下为了你能够不那么迷惑的写出Java语言的HelloWorld而需要知道的Java最重要的一些知识,虽然这些概念也仍然没有得到充分的讲解,不过第二章使你对Java中对象是怎么存在的有了一个基本的认识。
       下面就梳理一下第二章中比较重要和难懂的知识点。
       
      知识点1:P21,2.1,创建和操纵对象
      创建一个可以操控的对象
      Java创建对象的语句形如这样:new 类型(构建对象的参数-可选);,其中new为创建对象的关键字,类型可以是基本类型,包装器类型以及各种类类型,()里是参数列表,代表调用了构造函数,分号代表语句的结尾。这个语句创建了指定类型的对象。要想使用这个对象,必须要声明一个对象类型的引用,引用就是一个别名,Java里引用的概念和C++中引用的概念类似。比如String s = new String("1234");或者是String s = "1234";
      初始化
      第一句话定义并初始化了一个String类型的引用s,s被赋值为一个String类型的对象,这个对象被初始化为字符串字面值“1234”。
      这里发生了两次初始化,引用s的初始化,以及一个不具名的String类型对象的初始化。
      一个引用必须在使用(除了赋值操作的其他操作)之前初始化,一个对象创建的时候就一定被构造函数初始化过了,一个未经初始化的引用,其初值为null,使用值为null的引用会导致报错。
      内存布局
      引用和基本类型的对象都被存储到堆栈中。
      其他对象被存储到堆中。
      常量储存在代码内部。
      作用域和生命周期
      基本类型 和 对象引用 的作用域在它所在的语句块内,生命周期从它被定义到它所在的语句块结束。
      Java对象的作用域在它所在的语句块内,生命周期从它被定义到它被Java的自动垃圾回收机制回收。
 
     知识点2:P23,2.2.2,基本类型及其包装器类型
      基本类型有哪些
      boolean(布尔值),char(字符),byte(很小的整数),short(比较小的整数),int(整形),long(长整形),float(浮点数),double(高精度浮点数),void(空类型)。
      什么是包装器
      基本类型没有一个函数,不方便按照面向对象的思想向其传递信息,因此Java给每一个基本类型都对应了一个包装器类型,它们是一些类类型,在堆中被创建,可以执行各种方法,更方便使用。
      以上类型对应的包装器类型有哪些
     Boolean(布尔值),Character(字符),Byte(很小的整数),Short(比较小的整数),Integer(整形),Long(长整形),Float(浮点数),Double(高精度浮点数),Void(空类型)。
      基本类型的特点
     大小固定,存在堆栈中,过作用域就被释放,不是对象,不需要用引用操控,没有可以调用的函数,按值传递。
      包装器类型的特点
     大小固定,存在堆中,由GC释放,本身是对象,需要依赖引用操控,有函数可以调用,按引用传递。
      初始化基本类型的方法
      可以一开始就用字面值初始化,比如char c = 'c';
      也可以用另一个基本类型的值初始化,比如char ch = c;
      还可以用这个基本类型的包装器类型初始化基本类型的值,比如char ch2  = new Character('c');
      如果不在创建之初就初始化一个作为类成员的基本类型,则这个基本类型的变量会得到一个默认值,比如0。
      初始化 基本类型的包装器类型的引用 的方法
      可以一开始就用字面值初始化,比如Character c = 'c';
      也可以 先用字面值初始化这个包装器类型的对象,再用这个对象初始化这个包装器类型的引用,比如Character ch = new Character('c');
      还可以 先用基本类型的值初始化这个包装器类型的对象,再用这个对象初始化这个包装器类型的引用,比如Character ch = new Character(c);
 
      知识点3:P25,2.4,类类型
     什么是类类型
      类类型是一种区别于基本类型的类型分类,它允许Java使用者自己创造一些新的类型和这些类型的细节。
     创建和使用类类型
     创建类类型的代码看起来如下:class 类型名{ 类体 },其中,class是创建新类型的关键字,类型名是一个大写字母开头的名字,类体包括数据成员(属性)和函数成员(方法)。
     要想创建这个自定义类型的对象,需要使用下述语句:类型名 对象的引用名 = new 类型名(构造对象的参数-可选);
     可见自定义的类类型和Java已经存在的类类型的用法基本一致。
     什么是类属性/字段/数据成员/域(在特定语境下)
     字段(或者属性,数据成员,域)基本都是一个意思,是组成类的两个元素之一,它可以由一个或几个,可以是基本类型或引用类型,是用来存储类实例数据的类成员(或称“实例域”)。
     什么是类方法/类函数/函数成员
     类方法(method)是组成类的两个元素之一,可以有一个或多个,是用来操控修改数据成员的子程序(可复用程序片段),在Java中,没有全局的函数,一个函数(方法)必定属于某个类。
 
      知识点4:P27,2.5,类方法
     定义和使用类方法
      类定义一个类方法的语句形如 返回值类型 类方法名(参数列表){ 方法体 },必须定义在class后面的大括号里,其中,参数列表形如(类型1 参数1,类型2 参数2,etc)。如果返回类型不为void/Void,方法体中需显式地写出return语句来结束这个函数,形如return 返回值; 其中返回值可以为对象的引用或者基本类型的变量,也可以是一个字面值,一个常量。这个值的类型需和方法定义中的返回值类型一致或能非窄化地转换为返回值类型。比如double func(){return 1;},其中返回的1是int类型字面值,它转换到定义的double类型精度无损失(非窄化转换)。因此这样做可行。
     使用一个类方法,需要用点运算符。形如 对象引用.类方法名(想传入的参数);
     静态方法有特殊的调用方式,本章稍后就能看到。
 
     知识点5:P28,2.6.1,import是什么,怎么用
     包(Java类库)和名字冲突
      包就是一个Java类库,是程序员写好、打包好的一些类、方法的集合。
      我们在写Java程序中往往要根据一些已经实现好的功能做我们的新功能,而不是什么都自己开发。所以可以在你的Java编程IDE中进行设置来导入一个别人打包好的功能,通过在代码中使用import 这个包的名.具体的类; 来使用你导入的包中一个具体的类,或者使用import 这个包的名.*; 把这个语句放在一个Java文件的开头,来导入这个包中所有的类。大部分编译器都有编译优化,会只编译你用到的,不过仍然要说明这么一句,让编译器知道你可能会用到的内容。
        Java自己就提供了丰富的类库,其中java.lang类库是基础类库,每次都会被自动导入,而java.uitl非常常用,类似于C++中的SL,常用的写法是import java.util.*; 来导入java.util中的所有类。
       我们可能导入了多个包,不同的包中可能声明了同名的类,不过同一个文件中不能用同名的类,甚至在同一个方法下的不同作用域里,变量名都不能相同,所以显然无论你导入了几个包,它们中有几个你想用的同名的类,你都只能使用其中的一个。
      用import 这个包的名.具体的类; 就可以明确的告诉编译器,你想要使用哪个包里的这个名字的类。
      很多包名好像一个网站的网址倒过来写,比如com.google.xxxx(我随便举的例子)。这是因为网址是不重复的,不同的类库的开发者很难拥有同一个网址,所以Java希望你给包起名时用自己的网站倒过来的网址。试想你和另一个人都使用了com.baidu.useless作为包名,里面都有一个叫做Date的类,这两个类其实是不同的(但导入都要写成import com.baidu.useless.Date;),别的开发者拿到你们两个人的包想使用Date类时就会头疼了。
 
     知识点6:P29,2.6.3,static关键字
     静态成员的定义、性质和使用
      static关键字用于放在定义语句的类型前面,说明正在定义的字段/方法是静态的,被static修饰的类方法和类字段统称为静态成员。
      比起同一个类型的其他成员,所有这个类型的实例中的静态成员都是同一个对象,占用一个存储空间,它不依赖某个具体实例化的对象而存在,即使没有这种类型的对象,这种类型的静态成员也能被访问。
      举个例子,class StaticTest{ static int i = 47; } 在这个类中,i就是静态成员,而且被预初始化为47,直接在类体中写类似赋值的形式就行了,即使创建了两个StaticTest类的实例,比如StaticTest st1 = new StaticTest();StaticTest st2 = new StaticTest();,st1和st2中的i仍是同一个变量。
       你可以试着把st2中的i赋值成88,写下这个语句 st2.i = 88; ,之后你再检查st1.i的值,就会发现它也变成了88。
       访问StaticTest的静态数据成员有两种方法,一种是上述例子中的st1.i,通过点运算符,链接对象引用和对象内成员,这个常见的访问成员的方式对静态成员同样适用。还有一种访问方法是静态成员独有的,就是 类名.静态成员名 ,通过这种简单的方法你也可以访问到它。在StaticTest例子中想访问静态成员i还可以这么写:StaticTest.i,效果和 st1.i 或者 st2.i 是没有区别的。
        静态方法也可以这么访问,比如class StaticTest{ static void Func(){} },这个新的StaticTest类里定义了什么都不做的静态方法(静态函数成员)Func,可以使用StaticTest.Func();,在不创建StaticTest对象的情况下访问这个类内静态的函数。
 
     知识点7:P30,2.6.3,主函数
     主函数和HelloWorld
      在目前的很多IDE下,Java可以有多个主函数,不过你要选择其中的一个进行执行。主函数的写法很简单,不过因为Java的所有函数必须以类方法的形式存在,因此你需要先随便创建一个单独的类:
      public class MainTest{}
      class前面的public代表一种公开状态的访问权限,具体使用方法不在这章讲,总之要写成这样。MainTest可以起成任何别的你喜欢的名字,不过一个Java文件里只能有一个public修饰的类,而且这个Java文件的文件名必须和public修饰的类名保持一致。
      然后,在类中写一个特殊的方法:
      public class MainTest{
             public static void main(String[] s){
                   //主函数第一行,程序执行从这里开始
             }
      }
      就像这样,类必须是publc的,方法必须是public static的(如你所见,它们并列修饰一个函数时,public关键字要在static前面),返回值为void,参数类型是一个String数组s,用来存储命令行参数,就和其他的语言差不多。
      这样,一个主函数就写好了,书上给出了一个很简单的HelloWorld:
      import java.util.*;
 
      public class MainTest{
             public static void main(String[] s){
                   System.out.println("Hello World!");
                   System.out.println(new Date());
             }
      }
      让我们稍微分析一下这个HelloWorld,首先是import java.util.*;,导入java.util包中的所有类,我们下面用到了Date类,因此需要引入这个包。
      主函数里,System是一个类,out是System类的静态对象,println是out对象的静态方法,因此即使不创建System类,也能用点运算符访问它的对象,再用点运算符访问其对象的方法。
      println是一种很常见的方法,println可以向控制台输出它接收到的信息,比如一个字符串字面值"HelloWorld!"或者new Date(); 创建出的一个对象。
 
     知识点8:P32,2.8,三种注释
     C风格注释
      你可以把注释内容写在/* */里,可跨多行,一旦标记了/*,它遇到下一个*/就会结束注释。
      单行注释
      你可以把注释内容写在//标记之后,知道本行结束,不可跨多行。
      用于提取文档的注释(注释文档)
      Java提供了Javadoc机制从源码的注释中提取和生成html文档,不过这个注释要符合特定格式,
      用于提取文档的注释以/**开头,以*/结束。这样的注释也可跨多行,注释间每行开始如果有一个*或任意空白都可被忽略,为了更方便的提取文档,可以在这种注释里直接加入html代码,比如:
      /** 
      *具体类信息可以到这里查找<br>
      *<a href = "https://www.baidu.com"></a>
      */
      把这段注释文档放在某个你想注释的类,字段 或者 方法前面一行,再用javadoc提取文档你就能看到你对这个类/字段或者方法的注释,后面我们会提到,只能对标记为public或protect的类/域/方法做注释。
      也可以通过@符号加一些标记让javadoc快速自动帮你生成符合情境的html文档,比如:
      /**
      *@author TiriSane
      */
      提取文档后,这个注释对应的类/域/方法介绍上会有作者这一项。除了author还有很多标记可以用:
      @see 类名
      @see 类名#类成员名
      @see可以在你介绍一个类成员时,链接到其他类/类成员的文档。
      
      {@link 类名#类成员名 你想要显示的标签名}
      @link作用类似域@see,不过文档中超链接处会显示标签名而不是类名。
      
       @version
       用于说明版本信息。
       
       @since
       用于说明代码使用JDK的最早版本,比如@since JDK 1.8
       
       @author
       用于说明作者信息。
 
       @param 参数名 描述
       带@param的注释需放在一个类方法之前,用于说明这个方法的其中一个参数的意义。
 
       @return 描述
       带@return的注释需放在一个类方法之前,用于说明这个方法的返回值。
 
        @throws 异常类 异常说明
       目前还没讲到异常,简言之,就是用来 放在方法前 描述这个方法在何种情况下可能会产生这种异常。
 
        @deprecated
        用于指出注释文档所注释的类/域/方法是过时的,下个版本可能就删掉了,建议使用你代码的人不要使用这个标记的类/域/方法。
 
        {@docRoot}
        这个标签往往配合一些注释文档里的html代码使用,指出了文档的根目录,是一种相对路径标记。
        
        {@inheritDoc}
        这个标记所在的注释文档要放在类前面,生成javadoc时会把它的直接继承类的注释复制到这个类里。
 
         练习题:P37
        练习1:创建一个类,它包含一个int域(字段)和一个char域(字段),他们都没有被(显示地)初始化,将它们的值打印出来,以验证Java(对类内字段)执行了默认初始化。
 class Test
{
int i;
char c;
} public class MainTest {
public static void main(String[] args){
Test t = new Test();
System.out.println(t.i);
System.out.println(t.c);
}
}

练习1答案

    练习2:创建一个输入"Hello, World"的程序,编译和运行它。
 public class MainTest {
public static void main(String[] args){
System.out.println("Hello, World");
}
}

练习2答案

    练习3:找出本章含有ATypeName的代码段,将其改成完整的程序。
 class ATypeName{}

 public class MainTest {
public static void main(String[] args){
ATypeName a = new ATypeName();
}
}

练习3答案

    练习4、5:找出本章含有DataOnly的代码段,将其改写成一个程序,并把DataOnly的数据打印出来。
 class DataOnly{
int i;
double d;
boolean b;
} public class MainTest {
public static void main(String[] args){
DataOnly data = new DataOnly();
data.i = 47;
data.d = 1.1;
data.b = false;
}
}

练习4、5答案

         练习6:编写一个程序,使它含有本章定义的storage方法的代码段,并调用之。
 class Storage{
int Storage(String s){
return s.length()*2;
}
} public class MainTest {
public static void main(String[] args){
Storage st = new Storage();
st.Storage("1234");
}
}

练习6答案

         练习7:将和Incrementable相关的代码段改写成一个完整的可编译源代码。
 class StaticTest {
static int i = 47;
} class Incrementable{
static void increment(){ StaticTest.i++; }
} public class MainTest {
public static void main(String[] args){
Incrementable icmt = new Incrementable();
icmt.increment(); Incrementable.increment();
}
}

练习7答案

         练习8:编写一个程序,展示无论你创建了某个特定类型的多少个对象,这个类中的某个特定的static域只有一个实例。
 class StaticTest {
static int i;
} public class MainTest {
public static void main(String[] args){
StaticTest st0 = new StaticTest();
StaticTest st1 = new StaticTest();
StaticTest st2 = new StaticTest(); st0.i = 1;
st1.i = 2;
st2.i = 3; System.out.println(st0.i);
System.out.println(st1.i);
System.out.println(st2.i);
}
}

练习8答案

         练习9:编写一个程序,展示自动包装功能对所有的基本类型和包装器类型都起作用。
 public class MainTest {
public static void main(String[] args){
boolean b = true;
char c = '0';
byte by = 1;
short s = 2;
int i = 3;
long l = 4;
float f = 3.14F;
double d = 2.17; Boolean B = true;
Character C = '0';
Byte By = 1;
Short S = 2;
Integer I = 3;
Long L = 4L;
Float F = 3.14F;
Double D = 2.17;
Void V = null;
}
}

练习9答案

         练习10:编写一个程序,打印出从命令行获得的三个参数。为此,需要确定命令行数组中String的下标。
 public class MainTest {
public static void main(String[] args) {
System.out.println(args[0]);
System.out.println(args[1]);
System.out.println(args[2]);
}
}

练习10答案

         练习11:将AllTheColorsOfTheRainbow这个示例改写成一个程序,然后编译、运行。
 class AllTheColorsOfTheRainbow {
int anIntegerRepresentingColors;
void changeTheHueOfTheColor(int newHue) { }
} public class MainTest{
public static void main(String[] args){
AllTheColorsOfTheRainbow acr = new AllTheColorsOfTheRainbow();
acr.changeTheHueOfTheColor(1);
}
}

练习11答案

         练习12、13、14:略,javadoc操作相关。
         练习15:使用练习2的程序,加入注释文档,并用Javadoc提取出html文件查看效果。
 /**
* @author TiriSane
* @version 1.0
* @since JDK 1.8
*/
public class MainTest {
/**
* @author TiriSane
* @version 1.0
* @since JDK 1.8
* @param args 用于接收命令行参数
* @return 没有返回值
*/
public static void main(String[] args){
System.out.println("Hello, World");
}
}

练习15答案

         练习16:找到第5章的Overloading.java示例,并为它加入javadoc文档,之后用Javadoc提取出html文件查看效果。

 /**
* @author Bruce Eckel
* @version 1.0
* @since JDK 1.5
*/
class Tree{
int height;
Tree(){
System.out.println("Planting a seeding");
height = 0;
} Tree(int initialHeight){
height = initialHeight;
System.out.println("Creating new Tree that is " + height + " feet tall");
} void info(){
System.out.println(" Tree is " + height + " feet tall");
} void info(String s){
System.out.println(s + ": Tree is " + height + " feet tall");
}
} public class Overloading {
public static void main(String[] args){
for(int i = 0;i<5;i++){
Tree t = new Tree(i);
t.info();
t.info("overloaded method");
}
new Tree();
}
}

练习16答案

Java超简明入门学习笔记(一)的更多相关文章

  1. Java超简明入门学习笔记(零)

    Java编程思想第4版学习笔记(零) 前言          这个笔记本主要记录了我在学习Java编程思想(第4版,中文版)的过程中遇到的重难点及其分析.主要参考了C++11版本的C++语言,对比了它 ...

  2. Java超简明入门学习笔记(四)

    Java编程思想第4版学习笔记(四) 第六章 访问权限控制         访问权限控制是面向对象编程中的重要概念,它划分了类设计者和类使用者的界限.通过设置权限,它一方面告诉类设计者,哪个部分的修改 ...

  3. Java超简明入门学习笔记(三)

    Java编程思想第4版学习笔记(三) 第五章 初始化与清理(构造器和垃圾回收机制)         Java有和C++类似的构造函数来为新创建的对象执行初始化及完成一些特殊的操作,有的类数据成员可能会 ...

  4. Java超简明入门学习笔记(二)

    Java编程思想第4版学习笔记(二) 第三章 操作符 & 第四章 控制执行流程(流程控制语句)         第三章和第四章的内容主要是讲操作符和流程控制语句,Java的大多数操作符和流程控 ...

  5. Hadoop入门学习笔记---part4

    紧接着<Hadoop入门学习笔记---part3>中的继续了解如何用java在程序中操作HDFS. 众所周知,对文件的操作无非是创建,查看,下载,删除.下面我们就开始应用java程序进行操 ...

  6. Hadoop入门学习笔记---part3

    2015年元旦,好好学习,天天向上.良好的开端是成功的一半,任何学习都不能中断,只有坚持才会出结果.继续学习Hadoop.冰冻三尺,非一日之寒! 经过Hadoop的伪分布集群环境的搭建,基本对Hado ...

  7. Hadoop入门学习笔记---part2

    在<Hadoop入门学习笔记---part1>中感觉自己虽然总结的比较详细,但是始终感觉有点凌乱.不够系统化,不够简洁.经过自己的推敲和总结,现在在此处概括性的总结一下,认为在准备搭建ha ...

  8. Hadoop入门学习笔记---part1

    随着毕业设计的进行,大学四年正式进入尾声.任你玩四年的大学的最后一次作业最后在激烈的选题中尘埃落定.无论选择了怎样的选题,无论最后的结果是怎样的,对于大学里面的这最后一份作业,也希望自己能够尽心尽力, ...

  9. Scala入门学习笔记三--数组使用

    前言 本篇主要讲Scala的Array.BufferArray.List,更多教程请参考:Scala教程 本篇知识点概括 若长度固定则使用Array,若长度可能有 变化则使用ArrayBuffer 提 ...

随机推荐

  1. 如何在Python中让两个print()函数的输出打印在一行内?

    1.两个连续的print()函数为什么在输出时内容会分行显示? 解:print()中有两个默认参数sep和end,其中sep是代替分隔符,end是代替末尾的换行符,默认使用‘,’代替空格,且默认末尾加 ...

  2. Ubuntu 没有 无线网 RTL8821ce 8111 8186

    1.将ubuntu的linux内核版本更改到4.14(其他版本不兼容这个无线网卡的驱动) 1.1 找到内核版本 #到 Ubuntu网站http://kernel.ubuntu.com/~kernel- ...

  3. Codeforces 1173A Nauuo and Votes

    题目链接:http://codeforces.com/problemset/problem/1173/A 思路:模拟. AC代码: #include<bits/stdc++.h> usin ...

  4. 设置非阻塞的套接字Socket

    当使用socket()函数和WSASocket()函数创建套接字时,默认都是阻塞的.在创建套接字之后,通过调用ioctlsocket()函数,将该套接字设置为非阻塞模式.函数的第一个参数是套接字,第二 ...

  5. dubbo源码学习(二) : spring 自定义标签

    做dubbo的配置时很容易发现,dubbo有一套自己的标签,提供给开发者配置,其实每一个标签对应着一个 实体,在容器启动的时候,dubbo会对所有的配置进行解析然后将解析后的内容设置到实体里,最终du ...

  6. shell脚本输出九九乘法表

    #!/bin/bash#输出九九乘法表 for ((i=1;i<=9;i++)) do for ((j=1;j<=$i;j++)) do echo -n $j'x'$i=$(($i*$j) ...

  7. (补充)9.Struts2中的OGNL表达式

    OGNL表达式概述 1. OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写 * 所谓对象图,即以任意一个对象为根,通过OGNL可以访问与这个对象关 ...

  8. php array remove empty values

    print_r(array_filter($linksArray)); 參考 Remove empty array elements Remove Empty Array Elements In PH ...

  9. Redis探索之路(四):Redis的五种数据类型Set和ZSet

    一:Set无需集合 Set是个集合,string类型的无需集合,通过hash table实现,添加删除查找复杂度都是0(1).对集合我们可以取并集,交集和差集.通过这些操作我们可以实现sns中的好友推 ...

  10. Spring Boot 2.X 对 web 的开发支持(二)

    Spring Boot 2.X 对 web 的支持开发 上章节的 Spring Boot 的入门案例,我们感受到 Spring Boot 简单的配置即可运行项目. 今天了解 Spring Boot 对 ...