Basic Java⚓︎
约 6309 个字 342 行代码 预计阅读时间 36 分钟
Elementary Programming⚓︎
Identifiers⚓︎
标识符(identifiers) 是一个包括字母、数字、下划线 (underscores)(_)和美元符号($)的字符序列。
- 不能以数字开头
- 不能是保留字,
true、false和null也不行 - 可以使任意长度
- 区分大小写
Variables⚓︎
变量的声明、赋值和初始化语法和 C/C++ 基本一样。但不同之处在于
- Java 不区分变量的声明和定义
- Java 不会对方法内的局部变量赋予一个默认值,而使用未初始化的变量会发生编译错误
final⚓︎
final 的三种用法:
-
修饰变量:
-
final变量是不可改变的,但它的值可以在运行时初始化,也可以在编译时初始化,甚至可以放在构造函数中初始化,而不必在声明的时候初始化,所以下面的语句均合法: -
如果修饰类对象,表示这个变量不可再赋成其它对象,而不是表示这个对象不可更改
-
-
修饰方法:表示一个函数不可更改,也就是不能被重载了,而不是修饰返回值的
- 修饰类:表示整个类不能被继承了(自然里面的所有方法也相当于被加了
final)
注:Java 有保留字
const,但是没有任何实际功能。
Numerical Data Types⚓︎
数值基本类型:
| 类型 | 大小 | 最小值 | 最大值 |
|---|---|---|---|
byte |
1B | -128 | 127 |
short |
2B | -32768 | 32767 |
int |
4B | –2,147,483,648 | 2,147,483,647 |
long |
8B | –9,223,372,036,854,775,808 | 9,223,372,036,854,775,807 |
float |
4B | 大约 –3.4E+38,有 7 个有效位 | 大约 3.4E+38,有 7 个有效位 |
double |
8B | 大约 –1.7E+308,有 15 个有效位 | 约 1.7E+308,有 15 个有效位 |
Java 中整型的范围与运行 Java 代码的机器无关,解决了平台移植的问题。
读取数字:
nextByte() // reads an integer of the byte type
nextShort() // reads an integer of the short type
nextInt() // reads an integer of the int type
nextLong() // reads an integer of the long type
nextFloat() // reads a number of the float type
nextDouble() // reads a number of the double type
数值运算符和 C/C++ 基本一致,故不展开介绍。
特殊的数字格式:
Literals⚓︎
- 整数字面量
- 只能赋给整数变量
- 当字面量太大,变量无法容纳时将会发生编译错误
- 默认为
int类型;若要指定为long类型,需要在附加一个L或l后缀
- 浮点数字面量
- 默认为
double类型 - 若要指定为
float类型,需要在附加一个F或f后缀;或者附加一个D或d后缀来显式指定double - 特殊的浮点数值:
- 正无穷大:
Double.POSITIVE_INFINITY - 负无穷大:
Double.NEGATIVE_INFINITY - NaN(不是一个数字
) :Double.NaN- 用
Double.isNaN来判断是否是数字
- 用
- 正无穷大:
- 默认为
Type Casting⚓︎
实线表示无信息丢失的转换,虚线表示可能有精度损失的转换。
类型转换(type casting) 分为:
- 隐式(implicit) 转换:
- 类型拓宽:
double d = 3
- 类型拓宽:
- 显式(explicit) 转换:
- 类型缩窄:
int i = (int)3.0; - 小数部分截断:
int i = (int)3.9;
- 类型缩窄:
四舍五入:
Selections⚓︎
布尔类型:
- 关键字为
boolean,取值只有true和false - 整型与布尔值之间不能相互进行转换
- 所以当
even为boolean变量时,if (even != 0)是错的
- 所以当
if-else 语句:
- 基本上和 C/C++ 语法一致
-
else自己匹配相同块内最近的if语句
switch-case 语句:
- 基本上和 C/C++ 语法一致
switch语句必须得到char / byte / short / int类型的值case语句的值的类型必须和switch语句的值相同,且仅支持常量表达式(不能在表达式中包含变量)
条件运算符:boolean-expression ? exp1 : exp2
运算符优先级:
如果具有相同优先级的运算符彼此相邻,它们的结合性决定了计算顺序:除了赋值运算符(它是右结合的 (right-associative))之外,所有二元运算符都是左结合的 (left-associative)。
Mathematical Functions, Characters, and Strings⚓︎
java.lang.Math⚓︎
- 类常量:
PI、E -
类方法:
-
三角函数:
-
指数函数:
-
舍入函数:
-
max(a, b)和min(a, b) abs(a)- 随机函数:
random(),返回[0.0, 1.0)范围内的随机double值
-
Character Data Type⚓︎
- Java 字符使用 Unicode,占用 2 个字节,以
\u开头,表示为四个十六进制数字,从\u0000到\uFFFF - 递增运算符
++和递减运算符--也可以用于char变量,以获取下一个或前一个 Unicode 字符 -
字符和整数类型间的转换:
-
字符类方法:
String Type⚓︎
String实际上是 Java 库中的一个预定义类,被称为引用类型(reference type),而不是基本类型 (primitive type)-
String对象的简单方法(实例方法(instance methods)) :length() // 字符串长度 charAt(index) // 指定索引下的字符 concat(s1) // 和另一个字符串拼接 toUpperCase() // 所有字母转大写 toLowerCase() // 所有字母转小写 trim() // 移除字符串两边的空白字符- 调用实例方法的语法:
referenceVariable.methodName(arguments) - 非实例方法被称为静态方法(static methods),可以在不使用对象的情况下调用
- 调用实例方法的语法:
-
比较字符串:
equals(s1) // 两个字符串是否相等 equalsIgnoreCase(s1) // 同 equals,但大小写不敏感 compareTo(s1) // 字符串是否大于 s1,返回一个整数, // 正数/0/负数分别表示比 s1 大/相等/小 compareToIgnoreCase(s1) // 同 compareToIgnoreCase,但大小写不敏感 startswith(prefix) // 字符串是否以特定前缀开始 endsWith(suffix) // 字符串是否以特定后缀结束- 注意:使用
==是在比较两个引用是否指向同一个对象(一般不会这么做,往往是搞错用法了)
- 注意:使用
-
获取子字符串:
-
在字符串中寻找字符或子字符串
indexOf(ch) // 字符 ch 第一次出现时的索引,若没找到返回 -1 indexof(ch, fromIndex) // 从 fromIndex 开始,字符 ch 第一次出现时的索引,若没找到返回 -1 indexOf(s) // 字符串 s 第一次出现时的索引,若没找到返回 -1 indexOf(s, fromIndex) // 从 fromIndex 开始,字符串 s 第一次出现时的索引,若没找到返回 -1 // 寻找最后一次出现的索引,和前 4 个方法对应 lastIndexOf(ch) lastIndexOf(ch, fromIndex) lastIndexOf(s) lastIndexOf(s, fromIndex) -
字符串与数字的转换:
// 字符串 —> 数字 int intValue = Integer.parseInt(intString); double doubleValue = Double.parseDouble(doubleString); // 数字 -> 字符串 String s1 = String.valueOf(number1); String s2 = number2 + "";- 实际上
valueOf做的事是直接调用并返回该对象的toString()方法(除了null对象返回字符串"null") ,后者是由Object定义的- 对于自定义类,我们可以重写
toString()方法,返回一个包含人类可读的字符串,并能合理描述类的对象的String对象
- 对于自定义类,我们可以重写
- 实际上
-
+运算: -
块 (block) 语法(和 Python 类似
) :- 起止各一行
""",单独占一行(开头"""后不能直接跟内容) - 内容自动去掉公共前缀空白,保留相对缩进
- 起止各一行
-
String是不可变的(immutable),即创建String实例后内容无法再更改- 因此没有任何函数能改变字符串的内容
- 但声明为
String引用的变量可以在任何时候改变以指向其他String对象
为什么要“不可变”
- 并发 / 线程安全
- 无状态:对象只读,天然支持多线程共享,无需同步
- Race-free:写并发代码时不用担心“读到一半被修改”
- 哈希与索引
- hashCode 缓存:计算一次后缓存到字段
hash,后续HashMap/get直接复用,复杂度从 O(n) -> O(1) - Key 可信:作为
HashMap/HashSet的 key 时,中途内容变化导致哈希漂移的灾难不可能发生(对比char[]或StringBuilder)
- hashCode 缓存:计算一次后缓存到字段
- 字符串常量池(StringTable)
intern()复用:字面量自动入池,相同内容全局一份,节省堆内存- 地址比较:
"foo"=="foo"直接返回true,JVM 级别优化
- 安全性与完整性
- 类加载器隔离:类名、文件路径、权限字符串一旦传入就无法被篡改,防止“在 check 之后、use 之前”被恶意代码改掉
- 网络 / 文件句柄:
new URL("http://xxx")的协议、主机名不可变,避免校验后地址被替换
- 编译器 & JVM 优化
- 字符串折叠:编译期常量表达式
"a" + "b"直接变成"ab",减少运行时拼接 - 栈上优化:逃逸分析后不可变对象可拆成标量替换,消灭堆分配
- 共享子串:JDK 7 以前 substring 共享底层
char[](offset + count) ,避免复制;JDK 7 之后虽然改为复制,但仍保留不可变语义,让 JIT 放心做循环不变量外提等优化
- 字符串折叠:编译期常量表达式
-
除不可变的
String类型外,还有两个常用的可修改的字符串类型:StringBuffer:线程安全,同步,性能稍慢StringBuilder:非线程安全,不同步,性能更快
-
如果需要不断运算逐渐形成大的字符串,应该使用
StringBuffer
更多
String的 API 见官方文档。
Input: The Scanner Class⚓︎
使用步骤:
- 导入包:
import java.util.Scanner; - 创建对象:连接到输入流(如
System.in) - 读取数据:调用相应的方法获取输入
- 关闭资源:使用完毕后关闭流(良好的编程习惯)
例子
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
// 1. 创建 Scanner 对象,读取标准输入
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个整数:");
// 2. 读取数据
if (sc.hasNextInt()) {
int num = sc.nextInt();
System.out.println("你输入的是:" + num);
}
// 3. 关闭资源
sc.close();
}
}
常用读取方法:
next():读取下一个标记 (token),以空白符(空格、回车等)作为分隔nextLine():读取整行内容,直到遇到回车符nextInt():将下一个标记解析为int类型nextDouble():将下一个标记解析为double类型hasNext...():判断是否还有下一个指定类型的内容,常用于校验输入
注意事项
nextInt()与nextLine()混用问题:- 当先调用
nextInt()再调用nextLine()时,nextInt()只会读取数字,而数字后面的回车换行符会留在缓冲区;接着调用的nextLine()会直接读取到这个回车符,导致看起来好像“跳过”了输入 - 解决方法:在
nextInt()之后多写一行额外的nextLine()来消费掉那个多余的回车符
- 当先调用
- 性能问题:内部使用正则表达式进行匹配,虽然功能强大且方便,但读取速度较慢
- 如果对性能需求敏感,建议改用
BufferedReader以获得更好的性能
- 如果对性能需求敏感,建议改用
Formatted Output⚓︎
格式化输出语句:System.out.printf(format, items)。其中 format 是一个包含字符串和格式说明符的字符串。格式说明符(format specifier) 指定 item(可能是一个数值、字符、布尔值或字符串)应该如何显示,且都以百分号开始。
常用说明符:
%b:布尔值%c:字符%d:整数%f:浮点数%e:采用标准科学计数法的数字%s:字符串
占位符完整格式为:%[index$][标识]*[最小宽度][.精度]转换符。
%:占位符的起始字符,若要在占位符内部使用%,则需要写成%%[index$]:位置索引从 1 开始计算,用于指定对索引相应的实参进行格式化并替换掉该占位符[标识]:用于增强格式化能力,可同时使用多个[标识],但某些标识是不能同时使用的[最小宽度]:用于设置格式化后的字符串最小长度,若使用[最小宽度]而无设置[标识],那么当字符串长度小于最小宽度时,则以左边补空格的方式凑够最小宽度[.精度]:对于浮点数类型格式化使用,设置保留小数点后多少位转换符:用于指定格式化的样式,和限制对应入参的数据类型
字符、字符串的格式化:
- 占位符格式:
%[index$][标识][最小宽度][转换符] - 可用标识:
-:在最小宽度内左对齐,右边用空格补上
- 可用转换符:
s:字符串类型c:字符类型,实参必须为char或int、short等可转换为char类型的数据类型,否则抛IllegalFormatConversionException异常b:布尔类型,只要实参为非false的布尔类型,均格式化为字符串true,否则为字符串falsen:平台独立的换行符(与通过System.getProperty("line.separator")是一样的)
整数的格式化:
- 占位符格式:
%[index$][标识]*[最小宽度]转换符 -
可用标识:
-:在最小宽度内左对齐,不可以与0标识一起使用0:若内容长度不足最小宽度,则在左边用0来填充#:对 8 进制和 16 进制,8 进制前添加一个0,16 进制前添加0x+:结果总包含一个+或-号- 空格:正数前加空格,负数前加
-号 ,:只用于十进制,每 3 位数字间用,分隔(:若结果为负数,则用括号括住,且不显示符号
-
可用转换符:
b:布尔类型d:整数类型(十进制)x:整数类型(十六进制)o:整数类型(八进制)n:平台独立的换行符,也可通过System.getProperty("line.separator")获取
浮点数的格式化:
- 占位符格式:
%[index$][标识][最小宽度][.精度]转换符 - 可用标识:同整数
- 可用转换符:
b:布尔类型n:换行符f:浮点数型(十进制) ,显示 9 位有效数字,且会进行四舍五入a:浮点数型(十六进制)e:指数类型(如 9.38e+5)g:浮点数型没,比%f,%a长度短些,显示 6 位有效数字,且会进行四舍五入
日期时间的格式化:
- 占位符格式:
%[index$]t转换符 -
日期转换符:
c:星期六 十月 27 14:21:20 CST 2007F:2007-10-27D:10/27/07r:02:25:51 下午T:14:28:16R:14:28b:月份简称B:月份全称a:星期简称A:星期全称C:年前两位(不足两位补零)y:年后两位(不足两位补零)j:当年的第几天m:月份(不足两位补零)d:日期(不足两位补零)e:日期(不足两位不补零)
-
时间转换符:
H:24 小时制的小时(不足两位补零)k:24 小时制的小时(不足两位不补零)I:12 小时制的小时(不足两位补零)i:12 小时制的小时(不足两位不补零)M:分钟(不足两位补零)S:秒(不足两位补零)L:毫秒(不足三位补零)N:毫秒(不足 9 位补零)p:小写字母的上午或下午标记,如中文为“下午”,英文为 pmz:相对于 GMT 的时区偏移量,如 +0800Z:时区缩写,如 CSTs:自 1970-1-1 00:00:00 起经过的秒数Q:自 1970-1-1 00:00:00 起经过的毫秒
Loops⚓︎
while循环和do-while循环基本上和 C/C++ 一致-
带标签的
break语句,实现智能跳出语句块(而非跳入)- 标签可用在任何语句中,甚至在
if或块语句中
- 标签可用在任何语句中,甚至在
-
for循环除了有一套和 C/C++ 基本一致的版本外,Java 5 引入了更加简洁的for-in语法,用于数组和容器:
Methods⚓︎
方法(methods) 是一个被分组在一起以执行某一操作的语句集合。
- 方法签名(method signature):方法名和参数列表的组合
- 形式参数(formal parameters)(形参
) :方法头中定义的变量 - 实际参数(actual parameters)(实参
) :调用方法时传递的参数值 - 返回值类型
Java 方法是按值传参的,传递的是变量或引用的副本,所以在方法内对参数修改不会改变方法外的变量或引用。
方法的重载(overloading) 是指在一个类中可以定义多个名称相同但参数列表不同的方法。注意区分重载和重写(overriding):
| 特性 | 方法重载 | 方法重写 |
|---|---|---|
| 发生范围 | 同一个类中 | 父子类之间 |
| 方法名 | 必须相同 | 必须相同 |
| 参数列表 | 必须不同 | 必须相同 |
| 返回值 | 可以不同 | 必须相同(或其子类) |
| 判定时间 | 编译阶段 | 运行阶段 |
模糊调用(ambiguous invocation):有时对于方法的调用可能会有两个或更多可能的匹配,但编译器无法确定最具体的匹配,这是一个编译错误。
例子
public class AmbiguousOverloading {
public static void main(String[] args) {
System.out.println(max(1, 2));
}
public static double max(int num1, double num2) {
if (num1 > num2)
return num1;
else
return num2;
}
public static double max(double num1, int num2) {
if (num1 > num2)
return num1;
else
return num2;
}
}
Arrays⚓︎
数组(array) 是一种表示相同类型数据集合的数据结构。
Single-Dimensional Arrays⚓︎
-
数组变量声明:
-
创建数组:
arrayRefVar = new datatype[arraySize]; - 一步完成数组的声明和创建:
datatype[] arrayRefVar = new datatype[arraySize]; -
数组创建后长度固定不变,其值为
rrayRefVar.length;并且所有元素会被赋予默认值- 数值基础类型:
0 char类型:\u0000boolean类型:false
- 数值基础类型:
-
通过索引访问数组元素:
arrayRefVar[index],index范围为[0, arrayRefVar.length-1] -
数组初始化器(intializer):一步完成声明、创建和初始化的简写语法,注意一定要在一条语句内完成,不能拆成多句
-
数组分配在堆上
[]运算符被预定义为检查数组边界,而且没有指针运算,就不能通过a+1得到下一个元素main函数中的String[] args参数表示命令行参数;注意args[0]就是第一个参数,程序名没有存储在args中-
复制数组:
-
如果直接使用赋值语句(比如
list2 = list1;) ,那么这只是让list2同时引用了list1的对象,并没有真的创建数组副本(浅拷贝) -
正确实现(深拷贝
) :-
手动实现:新建数组,利用循环逐元素复制
-
System.arraycopy(sourceArray, src_pos, targetArray, tar_pos, length)函数- 例子:
System.arraycopy(sourceArray, 0, targetArray, 0, sourceArray.length);
- 例子:
-
Arrays.copyOf(array, length)方法- 第 2 个参数是新数组的长度,这个方法通常用来改变数组的大小
- 如果数组元素是数值型,则多余的元素被赋值为
0;若是布尔型,则赋值为false - 如果长度小于原始数组长度,则只拷贝前面的元素
- 如果数组元素是数值型,则多余的元素被赋值为
-
例子:
- 第 2 个参数是新数组的长度,这个方法通常用来改变数组的大小
-
-
-
匿名数组(anonymous array):数组没有对应的显式引用变量
- 例子
: printArray(new int[]{3, 1, 2, 6, 4, 2});(printArray()是自定义函数)
- 例子
-
虽说 Java 按值传递参数,但对于数组这种引用类型而言,复制到方法中的参数值是指向数组实际内容的引用,因此修改数组参数是会实际影响到数组内容的
MultiDimensional Arrays⚓︎
-
声明 / 创建二维数组:
-
数组初始化器:使用简写记号完成声明 + 创建 + 初始化
-
长度:
-
二维数组的每一行可以有不同的长度,这样的数组称为锯齿状数组(ragged array)
Wrapper Classes⚓︎
-
包装类(wrapper class):一个其对象包装或包含基本数据类型的类。当我们创建一个包装类的对象时,它包含一个字段,在这个字段中我们可以存储基本数据类型。换句话说,我们可以将基本值包装进包装类对象中。
-
包装类包括:
-
-
创建包装对象
- 所有包装类型的
public构造器 已在 JDK 9 被标记为@Deprecated(since="9", forRemoval=true),并在 JDK 16 正式移除;现在无论new Integer(123)还是new Double(3.14)都会编译失败 - 官方理由:
- 构造函数每次强制生成新对象,浪费内存
valueOf工厂方法利用内部缓存(-128~127 默认缓存在 IntegerCache)- 字符串驻留、
Boolean的true/false单例(类的对象有且仅有一个)保持一致语义 - 为未来值类型(Valhalla)铺路,彻底消灭包装对象
-
正确的做法:
- 所有包装类型的
-
自动装箱(auto-box) 和自动拆箱(auto-unbox):包装类型和对应的基础类型之间可以直接赋值
class Geeks { public static void main(String[] args) { int b; // Primitive data type Integer a; // Integer wrapper class b = 357; // assigning value to primitive a = b; // auto-boxing or auto wrapping converting primitive int to Integer object System.out.println("The primitive int b is: " + b); System.out.println("The Integer object a is: " + a); } } -
预生成对象(pre-created object):落在以下范围内,用的还是同一对象,否则就会创建新的对象
- 对于整数类型:-128~127
- 对于字符类型:0~127
- 对于布尔:
true和false - 对于浮点数:没有预生成对象
Classes and Objects⚓︎
-
定义一个类
- 在闭合花括号后没有
;(C++ 是有的) - 类的首字母大写,而变量或函数的首字母小写
- 函数体位于类的花括号内
- 每个成员要指定访问控制修饰符(比如
public) - 构造函数中没有初始化列表(好像是 C++ 特有的语法)
- 在闭合花括号后没有
-
编译单元(compliation unit)
- 每个编译单元必须有一个以 .java 结尾的名称,并且在该编译单元内部可以有一个
public类,该类必须与文件名相同 - 每个编译单元中只能有一个
public类 - 当你编译一个 .java 文件时,你会得到一个与文件名完全相同但扩展名为 .class 的输出文件,每个 .java 文件中的每个类都有一个这样的文件
- 一个可运行的程序是一堆 .class 文件
- 每个编译单元必须有一个以 .java 结尾的名称,并且在该编译单元内部可以有一个
-
创建一个对象:唯一的方式是使用
new运算符 -
赋值:赋值“从一个对象到另一个对象”是指将句柄从一个位置复制到另一个位置
-
按值传递
-
关系表达式
==,!=能处理任意对象,但是要注意- 对于基本数据类型,比较的确实是值
- 对于引用数据类型,比较的却是对象的内存的地址,即判断是否指向同一个对象
-
switch表达式- 从 Java 12 开始,引入了
switch表达式,它是对传统switch语句的增强 - 传统的
switch是语句(statement),只能执行分支逻辑;而新的switch表达式可以返回值,更加简洁、安全
int day = 3; String result = switch (day) { case 1 -> "Monday"; case 2 -> "Tuesday"; case 3 -> "Wednesday"; case 4 -> "Thursday"; case 5 -> "Friday"; case 6,7 -> "weekend"; // 多个值合并 default -> "Invalid day"; } ; System.out.println(result); // 输出 "Wednesday"- 使用
->箭头语法,避免了传统break的繁琐 switch表达式返回一个值,可以直接赋给变量- 支持多个
case合并(用逗号分隔) - 必须覆盖所有可能情况,否则需要
default -
yield:如果想在switch分支里写更复杂的逻辑,需要用yield返回值int day = 3; String result = switch (day) { case 1, 2, 3 -> { String prefix = yield prefix + day; "Early week: "; // 用 yield 返回结果 } case 4, 5 -> "Mid week"; case 6, 7 -> "Weekend"; default -> throw new IllegalArgumentException("Invalid day: " + day); }; System.out.println(result); // 输出 "Early week: 3" -
Java 的
switch表达式之所以不需要break,是因为switch表达式只会匹配其中一个case,一旦匹配上就执行箭头后面的操作,随后立即退出,不会检查后续的case了
- 从 Java 12 开始,引入了
-
成员初始化
- Java 竭尽全力确保任何变量在使用前都得到正确初始化
- 由于任何方法都可以初始化或使用该数据,所以强制用户在数据被使用前将其初始化为适当的值可能并不实际。因此,类的每个基本数据类型成员都会被保证获得一个初始值
0 -
指明初始化
-
初始化的顺序:在一个类中,初始化的顺序由类中定义变量的顺序决定(和 C++ 一样)
-
this是一个代理构造函数(delegating ctor)- 关键字
this产生被调用的对象方法的引用 this能显式调用另一个构造函数
- 关键字
-
清理 (cleanup):
finalize()- 当垃圾收集器准备释放用于存储对象的内存时,它将首先调用其
finalize()方法 - 但这个方法和 C++ 的析构函数截然不同
- 垃圾回收 != 析构
- 对象可能无法被垃圾回收
- 垃圾回收仅关乎内存
- 当垃圾收集器准备释放用于存储对象的内存时,它将首先调用其
Static Members⚓︎
- 静态成员当然还是类的成员
-
Class对象Class对象用于创建类中所有的“常规 (regular)”对象。每当创建一个新的类,也会创建一个单独的class对象(并且相应地会被存储在一个同名 .class 文件中)- 运行时,当想要创建某个类的对象时,执行程序的 JVM 首先会检查该类型的
Class对象是否已加载。如果未加载,JVM 将通过查找具有该名称的 .class 文件来加载它
-
初始化
- 静态成员将在被加载到类时初始化
- 初始化的顺序(举个例子)
- 第一次创建类型为
Dog的对象,或者第一次访问Dog类的静态方法或静态字段时,Java 解释器必须定位到 Dog.class - 随着 Dog.class 的加载,其所有的静态初始化器 (initializer) 都会被执行
- 当你创建一个新的
Dog时,Dog对象的构造过程首先在堆上为Dog对象分配足够的存储空间 - 该存储空间被清零,自动将该
Dog对象中的所有原始数据类型设置为它们的默认值 - 在字段定义点发生的任何初始化都会被执行
- 构造函数将被执行
- 第一次创建类型为
- 显式静态初始化:Java 允许在类中特别地“静态构造子句 (static construction clause)”(有时称为静态块 (static block))内分组其他静态初始化
- 显式初始化:Java 为每个对象初始化非静态变量提供了类似的语法
Packages⚓︎
Java 的包(packages) 是对程序的一种组织。
-
import-
import关键字用于引入整个库或该库的一个成员 -
包提供了一种管理“命名空间(name space)”的机制
-
-
静态导入
-
定义一个包
- 回忆一下:包提供了一种管理“命名空间(name space)”的机制
-
库也是一堆这样的类文件
-
如果有人想使用
MyClass,那 ta 就得必须使用import关键字来使mypackage中的名称可用
-
CLASSPATH- 将特定包的所有 .class 文件放入单个目录中
CLASSPATH包含一个或多个用作搜索 .class 文件的根目录- 或可以使用
-cp选项在 java 命令中使用
-
和 C/C++, Python 比较
编程语言 语法 实现 C/C++ #include <stdio.h>文本插入,编译时只看原型,链接时需要编译后的二进制代码 Java import java.util.Scanner;装载类,用 RTTI 了解类,编译和运行时均需要编译后的二进制代码,会自动编译 Python import Pandas装载运行 Pandas.py 文件,需要源码可见
Access Control⚓︎
-
Java 的访问说明符 (access specifiers)(在类的每个成员(无论是字段还是方法)的每个定义前放置
) :-
"friendly"
- 没有任何修饰符,那么该成员能被同一个包内所有的类访问
- 默认包 (default package):所有未在任何包中声明且位于同一文件夹中的类都在默认包中
-
public:谁都可以访问 private:只允许相同类访问protected(有些 "friendly") :派生类以及同一个包内的类能够访问
-
-
类的访问:
- 每个类都有一个访问控制修饰符
- 每个编译单元(文件)只能有一个公共类
- 公共类的名称必须与文件名完全匹配
- 虽然不典型,但可以有完全没有公共类的编译单元,在这种情况下可以随意命名文件
评论区



