Java 9 REPL (JShell)
REPL(Read-Evaluate-Print-Loop) —— 交互式解释器环境。
意为: 读取-计算-打印循环。
其流程为:输入值,交互式解释器会读取输入内容并对它求值,再返回结果,并重复此过程。
而 JShell 是 Java 9 引入的一个 REPL 工具,其为 Java 提供了一个交互式的编程环境工具。
Note!在Java Core 2.4 节第一次看到的,觉得 JShell 非常方便,就写在这里了!
Java 注释
Java 注释分为三种:
- 单行注释: 以
//
开头,//
之后的内容都会被注释掉。// 这是一个单行注释
- 多行注释: 以
/*
开头,以*/
结尾,之间的内容都会被注释掉。/* * 这是一个多行注释 * 这是一个多行注释 * 这是一个多行注释 * 中间的 '*' 只是为了好看一些而被大家遵守的约定 */
- JavaDoc 注释: 文档注释,以
/**
开头,以*/
结尾,可以在之间写一些描述性的信息,帮助 javadoc 生成 API 帮助文档。/** * @author:作者 * @version 版本 * @since 最小支持的 JDK 版本 * @see 参考转向(相关主题) * @docroot:表示产生文档的根路径 * @deprecated:不推荐使用的方法 * @param 方法参数类型 * @return 方法返回类型 * @exception 对方法可能抛出的异常进行描述(推荐描述 Runtime 异常) * @throws 对方法可能抛出的异常进行描述(推荐描述 非 Runtime 异常) */
Note!一些有趣的代码注释可以搜索有趣的代码注释
基本数据类型
Java 是一种强类型语言,这意味着必须为每一个变量声明一种类型。
Java 的数据类型
Note!Java 确定了每种基本类型的内存占用大小,这些大小不会像其他一些语言那样随着机器环境的变化而变化。这种不变性也是 Java 更具可移植性的一个原因。
Note!基本类型变量不是使用 new
创建变量,而是使用一个 “自动” 变量,这个变量直接存储 “值(Value)”,并置于栈内存中,因此更加高效。
数字
JDK 7 之后,数字间可以用下划线 _
分隔,提高可读性。
jshell> int num1 = 123_456;
num1 ==> 123456
jshell> long num2 = 123_3454355_345L;
num2 ==> 1233454355345
jshell> byte num3 = 1_20;
num3 ==> 120
jshell> float num4 = 123_63724_2.24F;
num4 ==> 1.2363724E8
jshell> double num5 = 12_34.45;
num5 ==> 1234.45
整数
Note!在创建 long
型整数时,为避免小写 l
与数字 1
混淆,推荐在数字后跟大写 L
。例:long num = 10L;
- 十进制表示:正常表示
jshell> int num = 10; num ==> 10
- 二进制表示:二进制以
0b
开头,后面跟二进制数jshell> int num = 0b10; num ==> 2
- 八进制表示:八进制以
0
开头,后面跟八进制数jshell> int num = 010; num ==> 8
- 十六进制表示:十六进制以
0x
开头,后面跟十六进制数jshell> long num = 0x10L; num ==> 16
浮点数
Note!在创建 float
型浮点数时,为与 long
保持统一,推荐在数字后跟大写 F
。例:float num = 10.12345F;
Note!在创建浮点数变量时,如果数字后没有跟 F
或 f
后缀,则默认为 double
类型。如下:
jshell> float num = 10.12345;
| 错误:
| 不兼容的类型: 从double转换到float可能会有损失
| float num = 10.12345;
| ^-----^
浮点数存在精度问题,最后避免使用浮点数进行比较!
jshell> float num1 = 1F;
num1 ==> 1.0
jshell> double num2 = 1;
num2 ==> 1.0
jshell> num1 == num2
$3 ==> true
--------------------------
jshell> float num3 = 0.1F;
num3 ==> 0.1
jshell> double num4 = 0.1;
num4 ==> 0.1
jshell> num3 == num4
$6 ==> false
Note!在涉及银行或其它对精度要求特别高的业务时,使用 BigInteger
或 BigDecimal
(大数处理工具类) 进行处理。
Note!在 Java 中有两种类型的数据可用于高精度的计算。它们是 BigInteger
和 BigDecimal
。BigInteger
支持任意精度的整数,可用于精确表示任意大小的整数值,同时在运算过程中不会丢失精度。 BigDecimal
支持任意精度的浮点数。
字符型(char)
char
类型原本用于表示单个字符。char
类型的字面量值要用单引号括起来。
jshell> char ch1 = 'A'; // 'A' 是编码值为 65 的字符常量
ch1 ==> 'A'
Note!'A'
与 "A"
不同,'A'
是字符常量,"A"
是包含一个字符 A 的字符串。
char
类型的大小为 2 个字节,它的值可以表示为十六进制值,范围从 \u0000
到 \uFFFF
。
jshell> char ch1 = '\u2122'; // \u2122 表示商标符号 ™
ch1 ==> '™'
jshell> char ch2 = '\u03C0'; // \u03C0 表示希腊字母 π
ch2 ==> 'π'
Unicode 和 char 类型
Java 使用的字符集是 Unicode
字符集。
前面说 char
类型(2个字节)原本用于表示单个字符,但由于现在 Unicode
字符集的不断扩充,2 个字节已经不足以表示后来扩充的一些字符了,需要用 3 个或 4 个甚至更多字节来表示一些字符。
事实上,现在 Java 中的 char
类型描述的是 UTF-16
编码中的一个代码单元。
而 Java 语言所使用的用来对 Unicode
字符集进行编码的编码方式正是 UTF-16
。
UTF-16
编码方式同时结合了定长和变长两种编码方法的特点,要么是 2 个字节,要么是 4 个字节。也就是说,现在在 Java 中要表示一个字符,可能需要 1 个 char
,也可能需要 2 个 char
。
Note!关于 Unicode 和 char 的关系请参考我的另一篇博客:Java 中 Unicode 和 char 的关系
jshell> "a人".length()
$1 ==> 2
jshell> "a人".codePointCount(0, 2)
$2 ==> 2
jshell> "𠋥".length()
$3 ==> 2
jshell> "𠋥".codePointCount(0, 1)
$4 ==> 1
jshell> "𠋥".codePointAt(0)
$5 ==> 131813
jshell> Integer.toHexString(131813)
$6 ==> "202e5"
Note!强烈建议不要在程序中使用 char
类型,除非确实需要处理 UTF-16
代码单元。
最好将字符串(String
)作为抽象数据类型处理。
特殊字符的转义序列
转义序列 | 名称 | Unicode值 |
---|---|---|
\a | 响铃 | - |
\b | 退格 | \u0008 |
\f | 换页,将当前位置移到下页开头 | - |
\t | 制表 | \u0009 |
\v | 垂直制表 | - |
\n | 换行 | \u000A |
\r | 回车 | \u000D |
\" | 双引号 | \u0022 |
\’ | 单引号 | \u0027 |
\\ | 反斜杠 | \u005C |
\0 | 空字符 | \u0000 |
\ddd | 1到3位八进制数所代表的任意字符 | 三位八进制 |
\xhh | 1到2位十六进制所代表的任意字符 | 两位十六进制 |
Note!所有转义序列都可以出现在加引号的字符字面量或字符串中,只有转义序列 \u
还可以出现在加引号的字符字面量或字符串之外(其他所有转义序列都不可以)!如下:
❯ cat CharTest.java
public class CharTest {
public static void main(String\u005B\u005D args) { // 转义序列 \u 可以出现在加引号的字符字面量或字符串之外
System.out.println("Hello!\nWorld!"); // 所有转义序列都可以出现在加引号的字符字面量或字符串中
}
}
❯ java CharTest.java // 在 JDK 11 中,单个 Java 源文件不再需要 javac 命令
Hello!
World!
Note!Unicode 转义序列会在解析代码之前得到处理。
例如:"\u0022 + \u0022"
并不是一个由一对双引号("
)括起来的字符串:\u0022 + \u0022
,而是会解析成 "" + ""
形成的一个空串。如下:
❯ cat CharTest.java
public class CharTest {
public static void main(String\u005B\u005D args) {
System.out.println("->" + "\u0022 + \u0022" + "<-"); // Unicode 转义序列会在解析代码之前得到处理
System.out.println("Hello\u0022 + \u0022World!");
}
}
❯ java CharTest.java
-><-
HelloWorld!
Note!需要当心注释中的 \u
。
如果注释中出现 \u000A
则会产生语法错误,因为 \u000A
会被提前解析为换行符 \n
。
同时如果在注释中 \u
后面没有跟4个十六进制数,也会产生语法错误。如下:
❯ cat CharTest.java
public class CharTest {
public static void main(String\u005B\u005D args) {
System.out.println("Hello World!"); // 这是一个 \u000A 的测试
}
}
❯ java CharTest.java
CharTest.java:3: 错误: 不是语句
System.out.println("Hello World!"); // 这是一个 \u000A 的测试
^
CharTest.java:3: 错误: 需要';'
System.out.println("Hello World!"); // 这是一个 \u000A 的测试
^
2 个错误
错误: 编译失败
❯ cat CharTest.java
public class CharTest {
public static void main(String\u005B\u005D args) {
System.out.println("Hello World!"); // 这是一个 \u 的测试
}
}
❯ java CharTest.java
CharTest.java:3: 错误: 非法的 Unicode 转义
System.out.println("Hello World!"); // 这是一个 \u 的测试
^
1 个错误
错误: 编译失败
数据类型转换
基本数据类型相互转换
基本数据类型转换原则
低 ---------------------------------------------- 高
byte, short, char -> int -> long -> float -> double 浮点数优先级 > 整数优先级
低级数据
⇒高级数据
==> 自动转换高级数据
⇒低技数据
==> 强制转换(通过在变量前加(目的类型)
实现)
public class TypeCast {
public static void main(String[] args) {
System.out.println("低 -> 高");
byte num1 = 120;
int num2 = num1;
System.out.println(num1);
System.out.println(num2);
System.out.println("高 -> 低");
long num3 = 233L;
int num4 = (int) num3; // 强制转换
System.out.println(num3);
System.out.println(num4);
System.out.println("低 -> 高 & 整数 -> 浮点数");
long num5 = 12345678L;
float num6 = num5;
System.out.println(num5);
System.out.println(num6);
System.out.println("高 -> 低 & 浮点数 -> 整数");
float num7 = 1234.567F;
long num8 = (long) num7; // 强制转换
System.out.println(num7);
System.out.println(num8);
}
}
/* 输出如下:
低 -> 高
120
120
高 -> 低
233
233
低 -> 高 & 整数 -> 浮点数
12345678
1.2345678E7
高 -> 低 & 浮点数 -> 整数
1234.567
1234
*/
不能对 boolean
类型数据进行类型转换
类型转换时可能会存在内存溢出或精度丢失等问题
public class TypeCast {
public static void main(String[] args) {
System.out.println("高 -> 低 & 内存溢出");
int num9 = 128;
System.out.println(num9);
System.out.println((byte) num9);
System.out.println("高 -> 低 & 精度问题");
System.out.println((int) 23.45);
System.out.println((int) -23.4556F);
}
}
/* 输出如下:
高 -> 低 & 内存溢出
128
-128
高 -> 低 & 精度问题
23
-23
*/
char
类型和 int
类型的互相赋值
- 在
0 ~ 65535
范围内,char
和int
类型可以互相赋值 - 若显式定义
int
变量后,转换为char
需强制转换
public class Char2Int {
public static void main(String[] args) {
char a1 = 'a';
int a2 = 'a';
System.out.println(a1);
System.out.println(a2);
int A1 = 65;
char A2 = 65;
System.out.println(A1);
System.out.println(A2);
char B1 = 'B';
int B2 = B1;
System.out.println(B1);
System.out.println(B2);
int b1 = 98;
char b2 = (char) b1;
System.out.println(b1);
System.out.println(b2);
}
}
/* 输出如下:
a
97
65
A
B
66
98
b
*/
运算符对基本数据类型转换的影响
-
当用一个二元运算符连接两个值时(例如 n + f,n 是整数,f 是浮点数),要先将两个操作数转换为同一种类型,然后再进行计算。
—— Java 核心技术(卷 I) 3.5.3 节jshell> int n = 5; n ==> 5 jshell> float f = 1.0F; f ==> 1.0 jshell> int result = n + f; | 错误: | 不兼容的类型: 从float转换到int可能会有损失 | int result = n + f; | ^---^
-
赋值运算符(
=
、+=
、-=
等)会自动将计算结果进行强制类型转换。jshell> int n = 1; n ==> 1 jshell> float a = 0.5F; a ==> 0.5 jshell> float b = 0.8F; b ==> 0.8 jshell> n += a + b; $11 ==> 2 // 相当于 n += (int)(a + b) jshell> n n ==> 2
基本数据类型与包装类之间的自动转换
jshell> Integer a = 2; // 自动装箱
a ==> 2
jshell> int b = Integer.valueOf(3); // 自动拆箱
b ==> 3
String 与其他类型数据之间的转换
jshell> "My age is " + 14
$16 ==> "My age is 14"
jshell> 14 + " is my age"
$17 ==> "14 is my age"
jshell> 7 + 5 + " is my age"
$18 ==> "12 is my age"
jshell> 7 + 5 + " is my age, my age is " + 12
$19 ==> "12 is my age, my age is 12"
当将一个字符串与一个非字符串的值进行拼接时,后者会转换成字符串。
变量
变量三要素
- 变量类型
- 变量名
- 变量值
变量声明(并赋值)
变量类型 变量名 = 变量值;
- Java 中可以一行声明多个变量(但不推荐一行声明多个变量)
int a = 1, b = 2, c = 3; a ==> 1 b ==> 2 c ==> 3 int a, b, c; a ==> 0 b ==> 0 c ==> 0
Note!Java 是大小写敏感的。
public class CaseSensitive {
public static void main(String[] args) {
System.out.println("Java 是大小写敏感的:");
String man = "Jack";
String Man = "John";
System.out.println("man: " + man);
System.out.println("Man: " + Man);
System.out.println("==================");
String woman = "Rose";
// String woman = "Mei";
System.out.println("woman: " + woman);
}
}
/* 输出如下:
Java 是大小写敏感的:
man: Jack
Man: John
==================
woman: Rose
*/
按照作用域划分,Java 中有 3 种变量
- 类变量: 独立于方法之外的变量,用 static 修饰,再用 final 修饰的话就是常量 (修饰符间没有顺序,谁前谁后都可以)。
- 实例变量: 可以不初始化,实例创建时,会自动将实例变量初始化为所属类型的默认值(
数字 ⇒ 0
,boolean ⇒ false
,引用类型 ⇒ null
)。 - 局部变量: 使用前必须声明和初始化值。
public class Variable{
static final String ZHANG_SAN = "zhangsan"; // final、static 为修饰符,不存在先后顺序
static int allClicks = 0; // 类变量
String str = "Hello, World"; // 实例变量
public static void main(String[] args) {
int num = 0; // 局部变量
System.out.println(num);
System.out.println(ZHANG_SAN);
}
}
运算符
自动升型
在运算中,运算结果类型会自动升为混合运算中类型最高的类型!
jshell> Integer.MAX_VALUE
$1 ==> 2147483647
jshell> Integer.MAX_VALUE + 1
$2 ==> -2147483648
jshell> Integer.MAX_VALUE + 1L
$3 ==> 2147483648
jshell> 1 + 1.0
$4 ==> 2.0
instanceof
instanceof 用于判断一个对象是否属于指定的类型。
class Human {}
class Man extends Human {}
public class InstanceOf {
public static void main(String[] args) {
Human human = new Human();
Man man = new Man();
System.out.println(man instanceof Man);
System.out.println(man instanceof Human);
System.out.println(man instanceof Object);
System.out.println(human instanceof Object);
}
}
逻辑运算符 | 短路运算
短路运算:
false && [Any...] // 后面没有执行
true || [Any...] // 后面没有执行
public class ShortOutOperation {
public static void main(String[] args) {
int c = 5;
boolean n = (c < 4) && (c++ > 5); // c < 4 为 false, 如果执行了后面,c 必然变成 6
System.out.println(n);
System.out.println(c);
int d = 5;
boolean m = (d > 4) || (d-- == 4); // d > 4 为 true, 如果执行了后面,d 必然变成 4
System.out.println(m);
System.out.println(d);
}
}
/* 输出如下:
false
5
true
5
*/
位运算
&
: 与|
: 或~
: 非^
: 异或- ⇒ 相同为0,不同为1
- ⇒ 可以理解为
异
: 相异为1(true),不相异为0(false) - ⇒
二进制加法
: 0+0=0,0+1=1
<<
: 左移 ⇒ *2 ⇒2 << 2
⇒ 23 = 8>>
: 右移 ⇒ /2 ⇒8 >> 2
⇒ 3√8 = 2
字符串连接 +
只要 +
两边任一边有字符串,就会进行连接。
jshell> "" + 10 + 20
$1 ==> "1020"
jshell> 10 + "" + 20
$2 ==> "1020"
jshell> 10 + 20 + ""
$3 ==> "30"
运算符优先级
不要刻意记忆,不确定的用括号 ()
包起来
参考资料
- 关于Java代码中注释的使用
- 生成javadoc时 @thorws 和 @exception 的区别
- Java-文档注释
- On Java 8 : 基本类型的存储
- Java Core I : 3.3 数据类型
- Java 基本数据类型
- 结合Java详谈字符编码和字符集
- Java为什么选择unicode字符集?字符编码的那些事
- 浅谈Unicode和char的关系(Java)
- Unicode 及编码方式概述
- 彻底弄懂 Unicode 编码
- unicode、utf-8、utf-16的理解 和 java的char类型所使用的编码
- java 语言使用的字符码集是 Unicode
- Java中的转义字符
- Java基础常见转义字符(完整归纳)