Java道经第1卷 - 第2阶 - 基础启航(二)
传送门:JB1-2-基础启航(一)
传送门:JB1-2-基础启航(二)
S04. 引用数据类型
心法:除了基本数据类型之外,都是引用数据类型,如类,数组,接口等。
引用数据类型使用 A a = new A()
格式进行声明赋值。
E01. 字符串类型
心法:String 底层是 char 数组,表示串字符,字符串值需要使用双引号包裹起来。
字符串所属 java.lang 包,该包下的所有类在使用时都无需导包,可直接使用。
1. 字符串声明赋值
心法:字符串有两种声明方式,目前阶段先理解为两种声明方式完全相同即可,后续学习到 String 池的概念后,再颠覆自己的认知也来得及。
String 标准声明格式: String 变量名 = new String("变量值")
String 简易声明格式: String 变量名 = "变量值"
武技:测试 String 字符串的声明赋值
package basic;
/** @author 周航宇 */
public class StringTest {
@Test
public void build() {
// 标准格式: String 变量名 = new String("变量值")
String strA = new String("hello strA!!!!");
System.out.println("strA = " + strA);
// 简易格式: String 变量名 = "变量值"
String strB = "hello strB!!!!";
System.out.println("strB = " + strB);
}
}
2. 字符串所占内存
心法:不同编码下,一个字符所占用的内存大小是不同的。
编码环境 | 一个中文字符占位(单位字节) | 一个英文字符占位(单位字节) |
---|---|---|
GBK | 2字节 | 1字节 |
UTF8 | 3字节 | 1字节 |
UTF16 | 4字节 | 4字节 |
武技:测试不同编码下,一个字符串所占字节数
package basic;
/** @author 周航宇 */
public class StringTest {
@Test
public void size() throws Exception {
System.out.println("a".getBytes("GBK").length);
System.out.println("中".getBytes("GBK").length);
System.out.println("a".getBytes("UTF-8").length);
System.out.println("中".getBytes("UTF-8").length);
System.out.println("a".getBytes("UTF-16").length);
System.out.println("中".getBytes("UTF-16").length);
}
}
3. 字符串编程接口
心法:应用程序编程接口 Application Programming Interface(API)是 Java 提供给调用方的一组方法,而 API 文档是 API 的说明文档,是一个 HTML 文件。
参考 JDK17官网API文档
API文档使用流程 | 描述 | |
---|---|---|
1看 | 方法名称 | Java 中使用分量符 .方法名() 进行方法的调用 |
2看 | 形式参数 | 方法要求我们传入怎样的形参,就传入什么类型的变量 |
3看 | 返回类型 | 方法返回给我们什么类型,就用什么类型的变量去接收,void 方法不接收 |
4看 | 静态修饰 | 非静态方法需要使用实例调用,静态方法直接使用类名调用 |
5看 | 方法作用 | 必须做到心中有数 |
String 常用 API 方法如下:
str.length()
:返回 str 包含的字符个数。str.isEmpty()
:返回 str 是否为空字符串,仅包含空格的字符串不视为空字符串。str.isBlank()
:返回 str 是否为空字符串,仅包含空格的字符串也视为空字符串。str.charAt(7)
:返回 str 中的 7 号位字符。str.equals("a")
:返回 str 是否和 a 相等,不忽略大小写。str.equalsIgnoreCase("a")
:返回 str 是否和 a 相等,忽略大小写。str.startsWith("a")
:返回 str 是否以 a 开头。str.endsWith("a")
:返回 str 是否以 a 结尾。str.indexOf("a")
:返回 str 中,第一次出现的 a 的位置。str.lastIndexOf("a")
:返回 str 中,最后一次出现的 a 的位置。str.substring(1, 5)
:截取 str 中,1 到 5 号位置的子串,左边包括右边不包括。str.concat("a")
:将 a 追加在 str 末尾。str.replace("a", "b")
:将 str 中全部的 a 替换为 b。str.contains("a")
:返回 str 中是否包含 a。str.repeat(7)
:将 str 重复拼接 7 次。str.split(",")
:将 str 按逗号拆分为字符数组。str.toLowerCase()
:返回 str 的全小写形式。str.toUpperCase()
:返回 str 的全大写形式。str.trim()
:返回 str 两端去空格(半角空格)之后的结果。str.strip()
:返回 str 两端去空格(全角空格)之后的结果。str.stripLeading()
:返回 str 头部去空格(全角空格)之后的结果。str.stripTrailing()
:返回 str 尾部去空格(全角空格)之后的结果。String.format("")
:格式化字符串。String.valueOf(7)
:将 7 转为字符串 7。
武技:测试字符串常用 API 方法
package basic;
/** @author 周航宇 */
public class StringTest {
@Test
public void api() {
// 返回指定该字符串的长度(char的个数)
System.out.println("length():\t\t\t" + "china中国\n\n".length());
System.out.println("length():\t\t\t" + "".length());
// 当且仅当该字符串长度为0时,才返回true
System.out.println("isEmpty():\t\t\t" + "HelloWorld".isEmpty());
System.out.println("isEmpty():\t\t\t" + "".isEmpty());
// 判断字符串是否为空白字符串,仅包含空格的字符串也视为空白字符
System.out.println("isBlank():\t\t\t" + "".isBlank());
System.out.println("isBlank():\t\t\t" + " ".isBlank());
// 返回该字符串指定索引处的字符,索引越界爆发 StringIndexOutOfBoundsException 异常
System.out.println("charAt():\t\t\t" + "HelloWorld".charAt(1));
// 将该字符串与指定字符串进行比较
System.out.println("equals():\t\t\t" + "HelloWorld".equals("HELLOWORLD"));
// 不考虑大小写,将该字符串与指定的字符串进行比较
System.out.println("equalsIgnoreCase():\t" + "HelloWorld".equalsIgnoreCase("HELLOWORLD"));
// 测试该字符串是否以指定的字符串前缀开头
System.out.println("startsWith():\t\t" + "HelloWorld".startsWith("Hello"));
// 测试该字符串是否以指定的字符串后缀结尾
System.out.println("endsWith():\t\t\t" + "HelloWorld".endsWith("World"));
// 返回第一次出现的指定子字符串在该字符串中的索引,不存在返回-1
System.out.println("indexOf():\t\t\t" + "HelloWorld".indexOf("o"));
// 返回最后一次出现的指定子字符串在该字符串内的索引,不存在返回-1
System.out.println("lastIndexOf():\t\t" + "HelloWorld".lastIndexOf("o"));
// 从指定位置截取该字符串,并返回一个子字符串,顾头不顾尾
System.out.println("substring():\t\t" + "HelloWorld".substring(0, 5));
// 将指定的字符串连接到该字符串的末尾
System.out.println("concat():\t\t\t" + "HelloWorld".concat("~~~"));
// 使用newChar替换该字符串中所有的oldChar,并返回最终的字符串
System.out.println("replace():\t\t\t" + "HelloWorld".replace("o", "*"));
// 当且仅当该字符串包含指定的字符串时,才返回true
System.out.println("contains():\t\t\t" + "HelloWorld".contains("He"));
// 将指定字符串重复拼接10遍后返回
System.out.println("repeat():\t\t\t" + "hello".repeat(10));
// 按指定的字符进行分割,分割结果中不包含指定字符
System.out.println("split():\t\t\t" + Arrays.toString("Hello World !!!".split(" ")));
// 将该字符串中的所有字符转换为小写
System.out.println("toLowerCase():\t\t" + "HelloWorld".toLowerCase());
// 将该字符串中的所有字符转换为大写
System.out.println("toUpperCase():\t\t" + "HelloWorld".toUpperCase());
// 对字符串两端去半角空格
System.out.println("trim():\t\t\t\t" + " hello world ".trim());
// 对字符串两端去半角/全角空格
System.out.println("strip():\t\t\t" + " hello world ".strip());
// 对字符串头部去半角/全角空格
System.out.println("stripLeading():\t\t" + " hello world ".stripLeading());
// 对字符串尾部去半角/全角空格
System.out.println("stripTrailing():\t" + " hello world ".stripTrailing());
// 使用指定的格式和参数对该字符串进行格式化,并返回最终的字符串
System.out.println("format():\t\t\t" + String.format("我叫%s,我今年%d岁了", "赵四", 58));
// 返回参数的字符串表示形式,相当于 (param + "") 操作
System.out.println("valueOf():\t\t\t" + String.valueOf(120) + 1);
}
}
E02. 正则表达式
心法:正则表达式 Regular Expression (RE) 是一个字符串表达式,用于对字符串进行匹配,对空格敏感。
普通字符:基础字符,包含数字和字母等,必须被中括号包裹:
[abc]
:必须是 abc 中的任意一个字符。[^abc]
:必须不是 abc 中的任意一个字符。[a-z]
:必须是 a~z 范围内的任意一个字符,包括两端值。[^a-z]
:必须不是 a~z 范围内的任意一个字符,包括两端值。
特殊字符:具有功能的特殊字符,Java 字符串中使用时需要进行转义:
.
:必须是除了换行符和回车符外的任意一个字符,等价于[^\\n\\r]
写法。\d
:必须是一个数字字符,等价于[0-9]
写法。\D
:必须不是一个数字字符,等价于[^0-9]
写法。\w
:必须是一个字符,数字或下划线,等价于[a-zA-Z0-9_]
写法。\W
:必须不是一个字符,数字或下划线,等价于[^a-zA-Z0-9_]
写法。\n
:必须是一个换行符。\r
:必须是一个回行符。\t
:必须是一个制表符。
限定字符:限定表达式出现的次数。
{n}
:表达式必须恰好出现 n 次。{n,}
:表达式必须至少出现 n 次。{n,m}
:表达式必须出现 n 到 m 次,包括两端值。*
:表达式必须出现任意次,等价于{0,}
写法。+
:表达式必须至少出现一次,等价于{1,}
写法。?
:表达式必须出现 0 或 1 次,等价于{0,1}
写法。
边界字符:限定匹配以指定内容开头或结尾的字符串:
^abc
:必须是以 abc 开头的字符串。abc$
:必须是以 abc 结尾的字符串。
1. RE正则匹配
常用 API 方法如下:
str.matches("RE")
:对字符串 str 进行 RE 匹配,返回布尔型结果,等价于Pattern.compile("RE").matcher(str).matches()
的写法。
武技:测试 RE 正则匹配
package basic;
/** @author 周航宇 */
public class RegularExpressionTest {
@Test
public void matches() {
// 测试普通字符
String regexA = "[ab][^c][e-h][^a-c]";
System.out.println("abfd".matches(regexA));
// 测试特殊字符
String regexB = ".\\d\\D\\n\\t\\r\\w\\W";
System.out.println("A9a\n\t\r_$".matches(regexB));
// 测试限定字符
String regexC = "\\d{2}[a-c]+[h-k]?";
System.out.println("14aaaah".matches(regexC));
}
}
2. RE正则替换
常用 API 方法如下:
str.replaceAll("RE", "a")
:将字符串 str 中满足 RE 规则的内容全部替换为 a 并返回替换后的字符串。
武技:测试 RE 正则替换
package basic;
/** @author 周航宇 */
public class RegularExpressionTest {
@Test
public void replaceAll() {
// 将str中满足RE规则的内容全部替换为 "*"
System.out.println("My Name Is 9527".replaceAll("[a-z]", "*"));
}
}
3. RE正则切割
常用 API 方法如下:
str.split("RE")
:将字符串 str 按照 RE 规则拆分成字符串数组,结果中不含 RE 内容。
武技:测试 RE 正则切割
package basic;
/** @author 周航宇 */
public class RegularExpressionTest {
@Test
public void spilt() {
// 将str按照RE规则拆分成字符串数组
String str = "Test A. Test B. Test C.";
// 数组必须使用 Arrays.toString(数组) 查看
System.out.println(Arrays.toString(str.split("\\. ")));
System.out.println(Arrays.toString(str.split("\\. *")));
}
}
4. RE常用模板
常用 RE 模板如下:
String TITLE_RE = "^.{1,42}$";
String TITLE_RE_MSG = "标题长度必须在1~42之间";
String AUTHOR_RE = "^.{1,42}$";
String AUTHOR_RE_MSG = "作者名称长度必须在1~42之间";
String CODE_RE = "^.{1,42}$";
String CODE_RE_MSG = "兑换口令长度必须在1~42之间";
String SN_RE = "^.{1,42}$";
String SN_RE_MSG = "订单编号长度必须在1~42之间";
String INFO_RE = "^.{1,170}$";
String INFO_RE_MSG = "描述长度必须在1~170之间";
String CONTENT_RE = "^.{1,170}$";
String CONTENT_RE_MSG = "内容长度必须在1~170之间";
String MENU_URL_RE = "^/[a-zA-Z]{0,256}$";
String MENU_URL_RE_MSG = "跳转地址必须以 / 开头,后续内容仅支持0~256个英文字母";
String MENU_ICON_RE = "^[a-zA-Z]{1,256}$";
String MENU_ICON_RE_MSG = "图标仅支持1~256个英文字母";
String USERNAME_RE = "^[a-zA-Z0-9]{4,20}$";
String USERNAME_RE_MSG = "账号必须由4到20个英文字母或数字组成";
String NICKNAME_RE = "^[\\u4e00-\\u9fa5_a-zA-Z0-9]{4,10}$";
String NICKNAME_RE_MSG = "昵称必须由4到10个中文,英文字母,数字或下划线组成";
String PASSWORD_RE = "^[a-zA-Z0-9]{4,20}$";
String PASSWORD_RE_MSG = "密码必须由4到20个英文字母或数字组成";
String REALNAME_RE = "^[\\u4e00-\\u9fa5]{2,6}$";
String REALNAME_RE_MSG = "真实姓名必须由2到6个中文组成";
String ID_CARD_RE = "^[1-9]\\d{5}(19|20)\\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$";
String ID_CARD_RE_MSG = "身份证号格式不正确";
String PHONE_RE = "^1(3[0-9]|4[01456879]|5[0-35-9]|6[2567]|7[0-8]|8[0-9]|9[0-35-9])\\d{8}$";
String PHONE_RE_MSG = "手机号码格式不正确";
String EMAIL_RE = "^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$";
String EMAIL_RE_MSG = "电子邮箱格式不正确";
String ZODIAC_RE = "^[\\u4e00-\\u9fa5]{2,4}$";
String ZODIAC_RE_MSG = "星座必须由2到4个中文组成";
String PROVINCE_RE = "^[\\u4e00-\\u9fa5]{2,20}$";
String PROVINCE_RE_MSG = "省份必须由2到20个中文组成";
String VCODE_RE = "^\\d{6}$";
String VCODE_RE_MSG = "验证码必须为6位数字";
E03. 运行数据区
心法:运行时数据区域 Runtime Data Area 是 JVM 中的一块内存区域,Java 程序在运行的时候,绝大部分的数据都在运行时数据区中活动。
1. 堆栈存储模式
心法:运行时数据区在逻辑上又被划分成 Stack 和 Heap 两块区域。
Java Stack 栈:相当于小区中的物业室,空间小,功能少,访问方便:
- 用于存储八大基本数据类型的真正值。
- 用于存储引用数据类型的内存地址(也称引用)。
Java Heap 堆:相当于小区中的住宅区,空间大,功能多,访问麻烦:
- 用于存储引用数据类型的真正值。
武技:测试引用数据类型的内存地址
package basic;
/** @author 周航宇 */
public class RuntimeDataAreaTest {
@Test
public void address() {
// 基本类型变量中存储的是数值
int money = 100;
System.out.println(money);
// 引用类型变量中存储的是内存地址
RuntimeDataAreaTest runtimeDataAreaTest = new RuntimeDataAreaTest();
System.out.println(runtimeDataAreaTest);
}
}
2. 比较内存地址
心法:比较内存地址有 == 符号和 equals() 方法两种方案。
==
:用来比较基本类型的数值和引用类型的内存地址。
equals()
:来自 Object 类,原本也是比较内存地址,而 String 类将其重写为比较内容。
武技:测试两种比较内存地址的方案的区别
package basic;
/** @author 周航宇 */
public class RuntimeDataAreaTest {
@Test
public void compare() {
// 基本数据类型的 ==,比较的是数值
double numA = 100.0;
int numB = 50 * 2;
System.out.println(numA == numB);
// 引用数据类型的 ==,比较的是内存地址
String strA = new String("abc");
String strB = new String("abc");
System.out.println(strA == strB);
// 引用数据类型使用equals比较值
String strC = new String("abc");
String strD = new String("abc");
System.out.println(strC.equals(strD));
}
}
3. 数据装箱拆箱
心法:八个基本数据类型都有相对应的引用数据类型,称为包装类。
装箱:基本类型转成对应包装类的过程,JDK1.5 后是自动装箱的。
拆箱:包装类转成对应基本类型的过程,JDK1.5 后是自动拆箱的,且对包装类进行数学计算的时候会自动触发拆箱操作。
基本数据类型 | 对应的包装类 | 手动装箱 | 手动拆箱 |
---|---|---|---|
byte | java.lang.Byte | new Byte(基本类型变量) | 包装类型变量.byteValue() |
short | java.lang.Short | new Short(基本类型变量) | 包装类型变量.shortValue() |
int | java.lang.Integer | new Integer(基本类型变量) | 包装类型变量.intValue() |
long | java.lang.Long | new Long(基本类型变量) | 包装类型变量.longValue() |
float | java.lang.Float | new Float(基本类型变量) | 包装类型变量.floatValue() |
double | java.lang.Double | new Double(基本类型变量) | 包装类型变量.doubleValue() |
boolean | java.lang.Boolean | new Boolean(基本类型变量) | 包装类型变量.booleanValue() |
char | java.lang.Character | new Character(基本类型变量) | 包装类型变量.charValue() |
武技:测试自动装箱和自动拆箱
package basic;
/** @author 周航宇 */
public class RuntimeDataAreaTest {
@Test
public void boxing() {
// 自动装箱: 基本类型 -> 引用类型(包装类)
Byte bigByte = 100;
Short bigShort = 200;
Integer bigInt = 300;
Long bigLong = 400L;
Float bigFloat = 1.0F;
Double bigDouble = 1.1;
Boolean bigBoolean = true;
Character bigChar = 'Z';
// 自动拆箱: 引用类型(包装类) -> 基本类型
byte smallByte = new Byte((byte) 100);
short smallShort = new Short((short) 200);
int smallInt = new Integer(300);
long smallLong = new Long(400L);
float smallFloat = new Float(1.0F);
double smallDouble = new Double(1.1);
boolean smallBoolean = new Boolean(true);
char smallChar = new Character('Z');
}
}
S05. 常用运算符号
E01. 基本运算符
1. 数学运算符
心法:数学运算符是最简单的运算符。
加法运算 +
:
- 若加号任意一端出现字符串则变为字符串拼接效果。
- 对一个数据类型的最大值加 1 后,会变为该类型的最小值。
减法运算 -
:对一个数据类型的最小值减 1 后,会变为该类型的最大值。
乘法运算 *
:无。
除法运算 /
:
- 若操作数都是整数,则除法运算的结果也是必然是一个整数。
- 任何数除以 0 都会抛出异常。
- 任何数除以 0.0 的结果都为无穷大,在 Java 中显示为 Infinity 内容。
取余运算 %
:
- 0 取余任何数都等于 0。
- 取余运算结果的正负号,只和第一操作数保持一致。
武技:测试数学运算符
package basic;
/** @author 周航宇 */
public class OperatorTest {
@Test
public void math() {
// 除法运算中,如果操作数都是整数,则结果也是整数
System.out.println(10 / 4);
System.out.println(10.0 / 4);
// 0取余任数都为0
System.out.println(0 % 3);
// 取余的结果的正负号只和左边的操作数一致
System.out.println(-5 % 3);
System.out.println(-5 % -3);
System.out.println(5 % -3);
// 任何数除以0.0都等于无穷大
System.out.println(1 / 0.0);
// 除数为0时会抛异常
System.out.println(1 % 0);
}
}
2. 赋值运算符
心法:赋值运算优先级最低。
赋值符号(等号):将等号符号后的字面量或变量中的常量赋值给等号前的变量中。
追算符号:
- 追加:
a += b
等价于a = a + b
,即将a + b
的结果重新赋值给 a 变量。 - 追减:
a -= b
等价于a = a - b
,即将a - b
的结果重新赋值给 a 变量。 - 追乘:
a *= b
等价于a = a * b
,即将a * b
的结果重新赋值给 a 变量。 - 追除:
a /= b
等价于a = a / b
,即将a / b
的结果重新赋值给 a 变量。 - 追模:
a %= b
等价于a = a % b
,即将a % b
的结果重新赋值给 a 变量。
武技:测试赋值相关运算符
package basic;
/** @author 周航宇 */
public class OperatorTest {
@Test
public void assign() {
int a = 1;
// a+=1 => a=a+1 => ++a
System.out.println(a += 5);
System.out.println(a -= 4);
System.out.println(a *= 3);
System.out.println(a /= 5);
System.out.println(a %= 7);
System.out.println(a);
}
}
3. 自运算符
心法:自运算优先级最高,且计算过程中不发生任何类型转换。
自增:a++
等价于 a = a + 1
,分为 a++ 先用后加和 ++a 先加后用。
自减:a--
等价于 a = a - 1
,分为 a-- 先用后减和 --a 先减后用。
武技:测试自我相关运算符
package basic;
/** @author 周航宇 */
public class OperatorTest {
@Test
public void self() {
int a = 1;
// 先用后加
System.out.println(a++);
// 先加后用
System.out.println(++a);
// 自运算不发生任何类型转换
short num = 100;
num = ++num;
System.out.println(num);
}
}
4. 关系运算符
心法:关系运算符不支持连续比较,且均返回布尔类型。
比较符号:
==
:用来比较基本类型的数值和引用类型的内存地址。>
:大于符号,大于等于符号使用>=
即可。<
:小于符号,小于等于符号使用<=
即可。!=
:不等于符号,Java中不支持<>
的不等符号写法。
武技:测试关系相关运算符
package basic;
/** @author 周航宇 */
public class OperatorTest {
@Test
public void relation() {
int a = 9;
// 不等式的关系运算符不允许连算
System.out.println(a > 11);
System.out.println(a == 11);
System.out.println(a != 11);
System.out.println(a < 11);
System.out.println(a <= 11);
System.out.println(a >= 11);
}
}
5. 逻辑运算符
心法:逻辑运算优先级: 非 > 与 > 或,且均返回布尔类型。
逻辑与 &&
:与中有假则假,与中全真才真,会发生短路现象。
逻辑或 ||
:或中有真则真,或中全假才假,会发生短路现象。
逻辑非 !
:非真即假,非假即真。
武技:测试逻辑相关运算符
package basic;
/** @author 周航宇 */
public class OperatorTest {
@Test
public void logic() {
int a = 7;
// 与中有假则假,与中全真才真
System.out.println(a < 9 && a == 7);
// 或中有真则真,或中全假才假
System.out.println(a == 9 || a != 7);
// 非真则假,非假则真
System.out.println(!(a == 9));
// 与的优先级高于或
System.out.println(a != 9 || a == 1 && a == 8);
System.out.println((a != 9 || a == 1) && a == 8);
// 逻辑运算有短路现象
System.out.println(false && a++ == 0);
System.out.println(a);
}
}
6. 位运算符
心法:位运算符直接操作比特位,效率更高。
位与 &
:与中有 0 则 0,与中全 1 才 1,不会发生短路现象。
位或 |
:或中有 1 则 1,或中全 0 才 0,不会发生短路现象。
按位取反 ~
:0 变 1,1 变 0,包括符号位。
按位异或 ^
:相同为 0,不同为 1:
- 按位异或操作等效于按位无进位相加。
- 按位异或操作满足交换律,即
a ^ b ^ c = a ^ c ^ b
。 - 按位异或操作满足结合律,即
(a ^ b) ^ c = a ^ (b ^ c)
。 - 按位异或操作中规定
N ^ 0 = N
以及N ^ N = 0
。
按位移动:
<<
:按位左移动,溢出忽略,用 0 右补,左移 N 位相当于乘以 2 的 N 次方。>>
:按位右移动,溢出忽略,用符号位左补,右移 N 位相当于除以 2 的 N 次方。>>>
:按位无符号右移动,溢出忽略,用 0 左补。
武技:测试位运算符
package basic;
/** @author 周航宇 */
public class OperatorTest {
@Test
public void bit() {
// 位运算符也可以用于逻辑判断,但不会发生短路现象
int a = 1;
System.out.println(false & a++ == 0);
System.out.println(a);
// 按位与有0则0
System.out.println("4 & 6计算补码: " + Integer.toBinaryString(4 & 6));
System.out.println("4 & 6计算真值: " + (4 & 6));
// 按位或有1则1
System.out.println("4 | 6计算补码: " + Integer.toBinaryString(4 | 6));
System.out.println("4 | 6计算真值: " + (4 | 6));
// 按位取反: 0变1,1变0
System.out.println("~4 计算补码: " + Integer.toBinaryString(~4));
System.out.println("~4 计算真值: " + ~4);
// 按位异或: 相同为0,不同为1
System.out.println("4 ^ 6计算补码: " + Integer.toBinaryString(4 ^ 6));
System.out.println("4 ^ 6计算真值: " + (4 ^ 6));
// 左移动: 溢出忽略,右边用0补位
System.out.println("-2 << 3计算补码: " + Integer.toBinaryString(-2 << 3));
System.out.println("-2 << 3计算真值: " + (-2 << 3));
// 带符号右移动: 溢出忽略,左边用符号位补位
System.out.println("-2 >> 3计算补码: " + Integer.toBinaryString(-2 >> 3));
System.out.println("-2 >> 3计算真值: " + (-2 >> 3));
// 无符号右移动: 溢出忽略,左边用0补位
System.out.println("-2 >>> 3计算补码: " + Integer.toBinaryString(-2 >>> 3));
System.out.println("-2 >>> 3计算真值: " + (-2 >>> 3));
}
}
E02. 三目运算符
心法:三目运算符,也叫三元运算符或三步运算符,支持嵌套。
公式:X ? Y : Z,表示当 X 为 true 时返回 Y,当 X 为 false 时返 Z。
要求:Y 和 Z 的类型必须一致,且三元运算符的返回值类型为 Y 或 Z 的返回值类型。
武技:测试三目逻辑运算符
package basic;
/** @author 周航宇 */
public class OperatorTest {
@Test
public void ternary() {
int num = -1;
// 公式: X ? Y : Z
String result = num > 0 ? "大于等于零" : "小于零";
System.out.println(result);
}
}
S06. 流程结构控制
E01. 选择结构
心法:选择结构也叫分支结构,表示多选一。
当大括号中仅一行代码时,可以省略大括号,但是并不建议这么写。
1. IfElse结构
心法:if 选择适用于范围条件匹配业务。
if 结构:
if (条件) {
代码;
} else if(条件) {
代码;
} else {
代码;
}
组成部分 | 描述 | 允许存在的数量 |
---|---|---|
if ( 条件 ) { 代码块 } | 当满足条件时进入代码块 | 必须有,而且只能有一个 |
else if ( 条件 ) { 代码块 } | 当满足条件时进入代码块 | 可以有 N 个,N>=0 |
else { 代码块 } | 当全部条件都不满足时进入代码块 | 要么只有一个,要么没有 |
武技:测试 IF/ELSE 选择结构
package structure;
/** @author 周航宇 */
public class ChooseTest {
@Test
public void ifElse() {
int score = 60;
// if(条件){满足条件时执行的代码}
if (score >= 60) {
System.out.println("你及格了...");
}
// if(条件){满足条件时执行的代码} else {其他情况}
if (score >= 60) {
System.out.println("你及格了...");
} else {
System.out.println("你没及格...");
}
// if(条件){满足条件时执行的代码} else if(条件) {满足条件时执行的代码} else {其他情况}
if (score >= 80) {
System.out.println("你很优秀...");
} else if (score >= 60) {
System.out.println("你及格了...");
} else if (score >= 0) {
System.out.println("你没及格...");
} else {
System.out.println("分数异常...");
}
}
}
2. Switch结构
心法:switch 选择适用于定值条件匹配业务。
switch 结构:
switch (表达式) {
case 匹配值:
代码;
break;
case 匹配值:
代码;
break;
default:
代码;
break;
}
组成部分 | 描述 |
---|---|
switch (定值) | 定值可选 byte/short/int/char/String 类型变量,表达式以及枚举类型 |
case 匹配值: 代码块 break; | 当定值匹配到匹配值时,进入代码块 |
default: 代码块 break; | 当全部匹配值都匹配失败时,进入代码块 |
switch 穿透现象:若 case 后无 break 则不会跳出,会继续按顺序向下执行,直到遇到 break 或所有代码执行完为止。
武技:测试 switch 选择结构
package structure;
/** @author 周航宇 */
public class ChooseTest {
@Test
public void switchCase() {
int num = 1;
// 括号中是表达式而不是条件
switch (num + 1) {
case 1:
System.out.println("你的数字是0...");
break;
case 2:
System.out.println("你的数字是1...");
case 3:
System.out.println("你的数字是2...");
break;
case 4:
System.out.println("你的数字是3...");
break;
case 5:
System.out.println("你的数字是4...");
break;
default:
System.out.println("数字不是1234中的一个...");
break;
}
}
}
3. Switch表达式
心法:从 JDK12 开始,支持了 switch 表达式,可以进一步简化 switch 结构,并使其可以拥有返回值。
switch 表达式结构:
返回值类型 变量名 = switch(表达式){
case 匹配值 -> 返回值;
case 匹配值, 匹配值 -> 返回值;
case 匹配值, 匹配值 -> {
yield 返回值;
};
default -> 返回值;
};
武技:测试 SWITCH 表达式用法
package structure;
/** @author 周航宇 */
public class ChooseTest {
@Test
public void switchExpression() {
int day = 8;
String result = switch (day) {
case 1, 2, 3, 4, 5 -> "工作日";
case 6, 7 -> "休息日";
default -> {
yield "输入有误";
}
};
System.out.println(result);
}
}
E02. 循环结构
心法:循环就是重复性做事,但必须是有逻辑有规律的事情才可使用循环。
当大括号中仅一行代码时,可以省略大括号,但是并不建议这么写。
无论那种循环,都满足初判变法则:
- 初: 初始化变量(只执行一遍)。
- 判: 循环判断条件(只有满足条件的时候才执行循环)。
- 变: 每次循环体结束之后将执行变量的变化。
1. While循环
心法:while 循环的特点是先判断后循环,当条件不满足时,循环体一次都不会执行。
while 循环结构:
while(条件) {
代码;
}
武技:测试 while 循环结构
package structure;
/** @author 周航宇 */
public class LoopTest {
@Test
public void whileLoop() {
// 初
int i = 1;
// 判
while (i <= 10) {
System.out.println(i);
// 变
i++;
}
}
}
2. DoWhile循环
心法:do-while 循环的特点是先循环后判断,即使条件不满足,循环体也会先执行一次。
do-while 循环结构:
do {
代码;
} while (条件);
武技:测试 do-while 循环结构
package structure;
/** @author 周航宇 */
public class LoopTest {
@Test
public void doWhileLoop() {
// 初
int i = 100;
do {
System.out.println(i);
// 变
i++;
// 判
} while (i <= 10);
}
}
3. For循环
心法:FOR 循环的初、判、变三要素均在一行编写,简单明了。
for 循环结构:
for(初; 判; 变){
代码;
}
武技:测试 for 循环结构
package structure;
/** @author 周航宇 */
public class LoopTest {
@Test
public void forLoop() {
// fori + tab
for (int i = 1; i <= 10; i++) {
System.out.println(i);
}
}
}
E03. 流控关键字
1. continue
心法:循环体中使用 continue 关键字可以立即结束本次循环,并开启下一次循环。
武技:测试 continue 流控关键字
package structure;
/** @author 周航宇 */
public class LoopTest {
@Test
public void continueLoop() {
for (int i = 0; i < 10; i++) {
if (i == 3) {
continue;
}
System.out.println(i);
}
}
}
2. break
心法:循环体中使用 break 关键字可以立即结束所有轮次的循环。
break 关键字可配合循环别名如 out: 跳出指定某一层循环。
武技:测试 break 流控关键字
package structure;
/** @author 周航宇 */
public class LoopTest {
@Test
public void breakLoop() {
out:
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i == 2) {
break out;
}
}
System.out.println(i);
}
}
}
S07. 数组数据类型
心法:数组 array 是内存中一段内存连续的线性数据结构,属于引用数据类型。
数组结构特点 | 描述 |
---|---|
拥有下标 | 数组的下标从 0 开始,支持使用 数组[索引] 的格式来操作数组元素 |
拥有默认值 | 数组中的每个最底层元素都具有默认值,如 0, 0.0, false, \u0000, null 等 |
定长 | 数组在创建之后,长度不可更改 |
增删慢 | 数组长度不可改变,导致增删操作必须使用创新数组,效率低 |
查询快 | 数组中每个元素的内存地址连续可计算,所以在明确位置的情况下,查询效率更高 |
E01. 一维数组
心法:一维数组中的每个元素都是一个单一数据类型,且全部元素的数据类型必须一致。
1. 内存分布
心法:数组在内存中分为两部分存储,变量名在栈中,值在堆中。
栈变量中存放的是数组中,第一个元素的内存地址,即数组首地址。
一维数组内存分布如图:
2. 声明赋值
心法:一维数组在声明时,
int[] arr / int []arr / int arr[]
效果相同。
数组创建原则:数组在创建的时候,要么指定内容,要么指定长度,但二者不可同时指定。
输出数组对象:
- 字符数组:可以直接输出值,对字符数组拼接空字符串或调用其 toString() 方法可获取其内存地址。
- 非字符数组:直接输出的结果为堆内存中,数组的首地址。
一维数组创建方式 | 示例 |
---|---|
仅指定长度 | int[] arr = new int[5] |
仅指定内容 | int[] arr = new int[]{5, 2, 77} |
仅指定内容(简化版) | int[] arr = {5, 2, 77} |
武技:测试一维数组的创建
package array;
/** @author 周航宇 */
public class OneArrayTest {
@Test
public void build() {
// 创建数组: 要么指定内容,要么指定长度,但不可同时指定
byte[] arrA = new byte[]{5, 2, 77};
long[] arrB = {1L, 3L, 5L, 7L, 9L};
char[] arrC = new char[3];
arrC[0] = 'B';
arrC[1] = 'O';
arrC[2] = 'Y';
// 直接对非字符数组进行打印,输出的是数组的内存地址
System.out.println(arrA);
System.out.println(arrB);
// 使用数组的索引取值
System.out.println(arrA[0]);
System.out.println(arrB[0]);
// 仅字符数组可以直接输出值,对其拼接字符串或调用toString()方法,可以获得内存地址
System.out.println(arrC);
System.out.println("" + arrC);
System.out.println(arrC.toString());
}
}
3. 数组遍历
心法:一维数组有 fori,for-each 和 Arrays 工具三种遍历方式。
FOR-I 遍历:适用于遍历过程中需要操作索引变量的场景:
for(int i = 0, j < 数组.length; i < j; i++){
System.out.println(数组[i]);
}
FOR-E 遍历:适用于遍历过程中无需操作索引变量的场景:
for(元素类型 e: 数组){
System.out.println(e);
}
Arrays工具:适用于仅查看数组中的全部元素的场景:
Arrays.toString(数组);
武技:测试一维数组的遍历
package array;
/** @author 周航宇 */
public class OneArrayTest {
@Test
public void iterator() {
String[] arr = {"你好", "世界"};
// 数组.length 返回数组中有多少个元素
// 利用普通的for循环进行遍历
for (int i = 0, j = arr.length; i < j; i++) {
System.out.print(i + "-" + arr[i] + "\t");
}
System.out.println();
// 如果需要从头到尾遍历,可以使用for-each循环
for (String e : arr) {
System.out.print(e + "\t");
}
System.out.println();
// 使用Arrays工具类查看数组中的元素
System.out.println(Arrays.toString(arr));
}
}
E02. 多维数组
心法:二维数组的本质也是一维数组,只不过其中的每个元素都是一个一维数组类型。
二维数组中元素的类型必须一致,但元素的长度可以不同。
1. 内存分布
心法:二维数组本身是内存连续的,但其指向的 N 个一维数组未必是内存连续的。
二维数组内存分布如图:
2. 声明赋值
心法:二维数组在声明时,
int[][] arr / int[] []arr / int[] arr[] / int [][]arr / int arr[][]
效果相同。
数组创建原则:数组在创建的时候,要么指定内容,要么指定长度,但二者不可同时指定。
输出数组对象:直接输出二维数组,打印的是该数组的内存地址,即使是字符数组也一样。
创建方式 | 示例 | 代码描述 |
---|---|---|
仅指定长度 | int[][] arr = new int[3][2] | 生成 3 行 2 列的数组 |
仅指定内容 | int[][] arr = new int[][]{ {5, 2}, {77} } | 生成 2 行 2 列的数组 |
仅指定内容(简化版) | int[][] arr = { {5, 2}, {77} } | 生成 2 行 2 列的数组 |
武技:测试二维数组的创建
package array;
/** @author 周航宇 */
public class TwoArrayTest {
@Test
public void build() {
// 创建数组: 要么指定内容,要么指定长度,但不可同时指定
byte[][] arrA = new byte[][]{{3, 1, 3}, {5}, {2, 77}};
long[][] arrB = {{3L, 1L, 3L}, {5L}, {2L, 77L}};
char[][] arrC = new char[2][3];
arrC[0][0] = 'H';
arrC[0][1] = 'I';
arrC[1][0] = 'B';
arrC[1][1] = 'O';
arrC[1][2] = 'Y';
// 直接输出二维数组,打印的是该数组的内存地址,即使是字符数组也一样
System.out.println(arrA);
System.out.println(arrB);
System.out.println(arrC);
// 使用数组的索引取值
System.out.println(arrA[0]);
System.out.println(arrB[1]);
System.out.println(arrC[1]);
System.out.println(arrA[0][0]);
System.out.println(arrB[0][1]);
System.out.println(arrC[0][2]);
}
}
3. 数组遍历
心法:二维数组有 fori,for-each 和 Arrays 工具三种遍历方式。
FOR-I 遍历:适用于遍历过程中需要操作索引变量的场景:
for(int i = 0, j < 数组.length; i < j; i++){
for(int m = 0, n < 数组[i].length; m < n; i++){
System.out.println(数组[i][m]);
}
}
FOR-E 遍历:适用于遍历过程中无需操作索引变量的场景:
for(元素类型 e1: 数组){
for(元素类型 e2: e1){
System.out.println(e2);
}
}
Arrays工具:适用于仅查看数组中的全部元素的场景:
Arrays.deepToString(数组);
武技:测试二维数组的遍历
package array;
/** @author 周航宇 */
public class TwoArrayTest {
@Test
public void iterator() {
int[][] arr = {
{3, 1, 3, 1, 44, 5, 6, 788, 3, 12},
{5, 2, 13, 4, 5, 56, 7},
{2, 77, 5, 45, 1, 3, 13}
};
// 使用ForI遍历二维数组
for (int i = 0, j = arr.length; i < j; i++) {
for (int m = 0, n = arr[i].length; m < n; m++) {
System.out.print(arr[i][m] + "\t");
}
System.out.println();
}
System.out.println("---------divider---------");
// 使用ForE遍历二维数组
for (int[] e1 : arr) {
for (int e2 : e1) {
System.out.print(e2 + "\t");
}
System.out.println();
}
// 使用Arrays工具类查看数组中的元素
System.out.println(Arrays.deepToString(arr));
}
}
Java道经第1卷 - 第2阶 - 基础启航(二)
传送门:JB1-2-基础启航(一)
传送门:JB1-2-基础启航(二)