提醒:全文约6000字,是一份比较单纯的学习笔记,知识点基本采用条目的形式列出,起到查漏补缺和备忘录的作用,而对内容之间的逻辑结构并未进行仔细梳理。本文内容包括:
- Java的基本程序设计结构
- 面向对象程序设计
文章目录
- 第三章 Java的基本程序设计结构
- 1、变量与运算
- 2、字符串
- 3、输入输出
- 4、控制流程
- 5、大数
- 6、数组
- 第四章 面向对象程序设计
- 1、概述
- 2、使用预定义的类
- 3、自定义类
- 4、静态字段与静态方法
- 5、方法参数
- 6、对象构造
- 7、记录
- 8、包
- 9、JAR文件
- 10、文档注释
- 11、类的设计技巧
第三章 Java的基本程序设计结构
1、变量与运算
-
标准命名:驼峰命名法,类名的每个单词首字母大写,如
HelloWrold
。 -
回车不是语句的结束标志,
;
才是。 -
静态成员函数 [p27]
-
注释:
//
单行注释,/* */
多行注释 -
基本类型:4整(int, short, long, byte),2浮(float, double),1 布尔(boolean)
-
十六进制表示浮点字面量 [p30]
-
NaN不是一个数
-
浮点不适用于无法接收舍入误差的金融计算,可以用 BigDecimal 类。
-
char:可以用转义序列
\u0000~\uffff
表示char值,\u
转移序列会在解析代码前处理,相当于文本替换。Java处理Unicode字符集,2个代码单元。[p32] ? -
boolean:不能与整型互转。
-
变量:Java变量命名不止英文字母 [p33];
var
可以根据初始值自动进行变量类型推断,但使用时需注意java的版本。 -
final:常量关键字,
final int a = 5
;public static fanal int a = 5
相当于一个全局变量。 -
枚举:
enum Size {SMALL, MID, BIG}
; -
精度与可移植性 [p36]
-
println与sqrt:前者会处理
System.out
对象;而后者是静态方法,不处理任何对象。 -
静态导入:
import static java.lang.Math.*
,没看懂干嘛。 -
类型转换:合法转换 [p38],强制转换 [p39],小数转整数采用截断而不是舍入。
-
赋值:是个表达式,可以嵌套,如
int y = x += 4
。 -
条件表达式:如
x < y ? x : y
;switch
也有表达式用法,相对于前者仅提供两个选择的情况,它可以提供多个选择。[p41] -
位运算:and(&),or(|),not(~);逻辑运算符采用短路规则,而位运算不会。左移(<<),右移(>>)。
注:
>>>
用 0 填充高位,而>>
用符号位填充高位。运算符的优先级见 [p43]
2、字符串
- 取子串:
substring()
; - 拼接:1)
+
(其它类型自动转换为字符串类型);2)以分隔符连接,String.join
;3)复制同一个字符串repeat(x)
;[p45] - 不可变:字符串是不可变的对象,要修改字符串,应通过 提取子串 --> 拼接 的方式。不过,编译器有字符串共享的优化机制。但是,只有字符串字面量才能共享。
- 比较:
s.equals(t)
;[p46] 注意不能使用==
比较两个字符串是否相等。注意空串与null
不相同。 - 码点:String是个char值(码点)的序列,
length()
、charAt()
返回的都是代码单元,而有些码点是占两个代码单元。应避免使用char [p48] - 构建字符串:
String builder
类,可以避免每次拼接都创建一个新的字符串。[p53] - 文本块:使用一对
"""
包围,用于多行的字符串,且会自动取出公共缩进。
3、输入输出
-
读取输入:使用
Scanner
对象,然后采用in.next()
方式提取数据。读密码:使用
Console
类,输入时不会以明文显示。[p57] -
格式化输出:1)C语言风格,
System.out.printf();
2)创建格式化字符串,String.fromat();
3)s.formatted();
,在java15中。 [p60] -
文件:1)输入,
Scanner(Path p, String encoding);
2)输出,PrintWriter(String filename, String encoding);
[p61]
问题:如何控制文件输入的读、写、追加等模式?
4、控制流程
- switch,表达式 / 语句,有直通 / 无直通;一共有四种组合。[p73]
- 中断控制:
break
和continue
,都可以通过标签而用于嵌套循环。
问题:switch说明情况会触发多个分支?
5、大数
- 大整数:
BigInteger
类。 - 大浮点数:
BigDecimal
类,创建时建议使用字符串参数,因为传入double参数很容易产生舍入误差。
这两个大数类都只能通过调用方法来进行四则运算,而无法使用+,-,*,/
等运算符。
6、数组
-
初始化:
int[] a = new int[100]
;int[] a
与int a[]
的用法都是可以的。允许数组的长度为 0,但不同于 null,就像空字符串一样。
匿名数组?
-
遍历:使用
for each
循环可以遍历,其中被遍历者应当时有 Iterable 接口的类对象。 -
拷贝:Java的数组是实现在堆上的,
=
进行数组的赋值,将引用同一个数组。拷贝所有值应当使用Arrays.copyOf()
;[p83] -
命令行参数:main 方法的参数
String[] args
,args
可以接收从命令行传递的参数。如java Message -g cruel world
,则 args[0] 为 -g,args[1] 为 cruel,args[2] 为 world。 -
排序:
Arrays.sort
;是优化过的快速排序。 -
多维数组:
double a = new a[3][4];
Java中没有实际上的多维数组(与c++不同),而是数组的数组,因此,我们可以构造出不规则数组。[p89] 即多维数组并不是一个连续的内存块去存放每个元素。
// for-each进行数组的循环遍历
for (int element : a){
System.out.println(element);
}
第四章 面向对象程序设计
关于对象、对象变量、不可变对象的理解很关键。
1、概述
面向对象比面向过程更适合规模较大的问题。
- 类的封装:不让其它类直接访问本类的实例,目的是减少类之间的耦合。
- 类的继承:通过一个类扩展另一个类。
- 类之间的关系:1)uses-a;2)has-a;3)is-a;
2、使用预定义的类
-
有的类只封装了功能,而不必隐藏数据,因为根本没有数据。如
Math
类。 -
为什么使用类而不是像
int
一样的内置类型来表示日期?因为不同地区可能有不同的表示,使用类方便了改进和替换。 -
toString()
方法,用于生成一个类的字符串描述。 -
对象,对象变量:对象变量并不实际包含一个对象,它只是引用一个对象。对象本身是在堆中的。
-
关于历法的故事:《Calendrical Calculations》[p99]
-
静态工厂方法:通过
LocalDate.now()
这样的形式来生成一个对象。 -
“当然,封装的意义就在于内部的表示并不重要”,可是,人们学习的时候又经常会说去读源代码呢。
-
更改器方法:会更改对象状态。
访问器方法:只访问对象,不改变对象状态。
3、自定义类
-
公共类:一个源文件中只能有一个公共类,但是可以有多个普通类。
-
一次编译多个类:1)使用通配符,如
javac Employee*.java
;2)只编译一个类,编译器自动搜索用到的其它类。[p107] -
字段:建议类的所有字段都使用 private ,保持封装性。
-
构造器:它的调用总与
new
相关联,而不能对已经存在的对象调用构造器。 -
命名:局部变量不要与实例字段同名,因为前者会“遮蔽”后者,有时会导致比较隐秘的bug。
-
声明:可以使用
var
关键字生命局部变量(对象)。但一般不用于数值类型,以免需要注意0,0L,0.0
之间的这种区别。而参数或实例字段,则必须声明类型,而不能用var
。 -
null的处理:可以利用 Objects 类,如
name=Objects.requireNonNull(n, "The name can not be null")
;有 严格 / 宽松 处理的区别。[p110] -
隐式参数:一个类方法,除了参数列表传入的参数,还有类的实例字段作为隐式参数。可以用
this
关键字指示隐式参数,当没有命名冲突时,也可以不写该关键字。 -
内联方法? [p111]
-
字段public --> 访问器方法:好处是可以改变类的内部实现,而不影响其它类对该类的使用(就类似数据库的三级模式结构的效果)。此外,通过更改器方法对实例字段赋值,还可以在方法中进行错误检查。
注意:不要编写返回可变对象引用的访问器方法,这将破坏类的封装性。
-
一个类的方法可以直接访问该类的所有对象的私有属性。
-
私有方法:一些内部使用的辅助方法不应该成为公共接口的一部分。[p114]
-
final关键字:常用于修饰基本类型,或不可变对象。因为 final 仅保证对象变量不变(即一个变量不会转而引用其它对象),但无法阻止关于对象本身的更改,即用于修饰可变对象通常没啥意义。
有的 final 修饰的变量也是可以修改的,如
System.out
,因为它不是用 java 编写,从而绕过了 java 的规则。
4、静态字段与静态方法
- 静态字段:属于类,而不属于单个对象。
- 术语”静态“的历史。[p117]
- 工厂方法:从一个类中得到不同样式的格式化对象。[p117]
- main方法:可以给每个类都增加一段演示代码。
问题:对于工厂方法不太理解。
5、方法参数
- C++ 中有传值和传引用两种方法参数传递方式,而 Java 总是传值的,即方法会得到所有参数值的一个副本。这里同样需要注意对象和对象变量的区别,你可以在方法中修改对象的状态,而无法改变对象变量的值(即你在方法中让一个对象变量指向另一个对象,但对于调用者而言,这个对象变量仍然指向原来的那个对象)。
6、对象构造
- 重载:Java 中允许重载任何方法,包括构造器。但方法的返回类型不是方法签名的一部分。[p126]
- 默认字段初始化:在 无构造器 / 空构造器 的情况下进行。例如整型会被初始化为 0,对象变量会被初始化为 null;
- 在构造器中,可以使用
this(...)
来调用本类的另一个构造器(重载的)。因此,可以只需要一次公共构造代码? [p129] - 初始化字段:
- 在声明字段时就赋值,如
private int i = 0;
- 在构造器中赋值。
- 使用初始化块。在类中用一对花括号包裹起来。
- 在声明字段时就赋值,如
- 静态初始化块:在初始化块前面加上
static
关键字,它将在类的第一次加载时执行,而不是在每次创建对象的时候都执行。
public class A {
private static int i;
static {
i = 0;
}
}
- 生成随机数?[p131]
- 析构:Java 中会自动进行垃圾回收(例如当一个对象变成不可达时),而不需要向 c++ 一样显式地调用析构函数。但有时程序会使用内存之外的资源,如文件、句柄等,此时还是需要主动进行释放的。[p133]
感受:你可能会觉得在声明字段时赋值,与使用初始化块的作用是如此的相似,为何要有初始化块这种机制呢?其实我也感觉有些奇怪。可能要到实际的代码练习中才能体会到了。
7、记录
有时候,数据只是数据,而面向对象程序设计提供的数据隐藏有些碍事。
- 记录:状态不可变,而且公共可读。
- 相当于一个类自动定义了:1)实例字段(组件);2)构造器;3)访问器;4)三个方法:
toString
,equals
,hashCode
;[p135] - 不能添加非静态的实例字段。(保持状态不可变)
- 特点:更易读、高效,在并发中更加安全(为啥?)。
- 相当于一个类自动定义了:1)实例字段(组件);2)构造器;3)访问器;4)三个方法:
record Point(double x, double y) {
...
}
- 构造器:
- 标准构造器:默认有的,可以用于设置所有的实例字段。
- 自定义构造器:可以设置参数列表,以多进行一些处理,最后还是要调用标准构造器。
- 简洁构造器:没有参数,纯纯标准构造器的前奏处理。
8、包
- 从编译器的视角来看,嵌套的包之间是没有任何关系的。
- 编译器会在包中定位类,而生成的字节码中总是通过完整的包名引用其它类。
- 对比:
package, import
就类似 c++ 中的namespace, using
; - 将类放入包:将包名写在类的源文件的开头。且源文件必须放到与完整包名所匹配的路径中。
// .../hello/world/A.java
package hello.world;
public class A {
...
}
- 编译器处理文件,而解释器(虚拟机)处理类。 存在编译成功而无法运行的情况。[p141]
- 类的访问:
- public:本类可以被任意类使用。
- 不指定,默认:本类可以由同一包中的所有类使用(注意嵌套的包之间是没有关系的)。
- private:仅由该类内部访问。
- 包的静态导入:可以使用类的静态方法和静态字段,而不必加类名前缀。[p140]
import static java.lang.System.*;
out.println("hello"); // 而不再需要加System前缀
- 从基目录编译类:编译器处理文件,使用
/
;解释器加载类,使用.
;
javac com/mycompany/PayrollApp.java // 编译
java com.mycompany.PayrollApp // 运行
-
类路径:包含所有类文件的路径的集合。需要包含:1)基目录? 2)当前目录;3)jar文件。
设置类路径:1)
java -classpath
2)设置CLASSPATH
环境变量。
9、JAR文件
将应用程序打包,将目录结构的文件夹变成一个 zip 格式的压缩文件。
-
jar 命令行程序选项,见 [p147]
-
清单文件:描述归档文件(jar包)的特殊特性,在
META-INF/MANIFEST.MF
; -
执行:1)打包,并指定程序的入口点 [p148];2)启动
java -jar Myprogram.jar
。在windows中,双击 jar 文件关联启动的是
javaw -jar
命令,它不会打开 shell 窗口。 -
包装器:用于将 jar 文件变成平台的可执行文件,如 exe。[p149]
-
多版本jar文件:是基于某个特定 java 版本的程序 / 库可以使用不同版本的 jdk 运行。
感受:这一节看起来也感觉比较抽象,日后有机会再慢慢去理解吧。
10、文档注释
将代码和注释放在一个地方可以更好地保持一致性。JDK 包含了一个工具叫做 javadoc,可以由源文件生成一个 html 文档。java 的联机 API 文档就是对标准 java 类库的源代码运行 javadoc生成的。
文档注释使用/**...*/
,包含标记和后面的自由格式文本。标记以@
开始,自由格式文本中可以使用 html 标签。
-
常用注释:可以对类、方法、字段等进行注释,直接将注释写在代码前面即可。
-
包注释:如果想生成包注释,就需要在每一个包目录中添加一个单独的文件,包括两种:1)
package-info.java
的 java 文件;2)package.html
文件。 -
注释提取:从代码的注释中提取文档。[p155]
问题:注释提取的用法没看懂。
11、类的设计技巧
- 一定要保证数据私有。数据的表示形式很可能会改变,但它们的使用方式却不会经常发生变化。
- 一定要初始化数据。最好不要依赖于系统的默认值,而是应该显示地初始化所有变量。
- 不要在类中使用过多的基本类型。如果有多个相关的基本类型,可以将它们封装成一个对象。这样可以使类更加易于理解。
- 分解有过多职责的类。
- 类名和方法名要更够体现它们的职责。
- 优先使用不可变的类。类似 plusDays 的方法并不会修改对象,而是返回状态已经修改的新对象。在多线程间共享对象更加安全,防并发更改。