第3章 Java的基本程序设计结构
3.1 一个简单的Java应用程序
访问修饰符 public等
应用程序的全部内容必须放在类中
源代码文件名必须与公共类的名字相同
类的源文件中必须包含main方法,main方法必须是public,还必须是静态的
System.out是对象
println()方法输出之后换行
print()方法不换行
3.2 注释
有三种
- // 行注释
- /* */ 块注释
- /** */ 用于自动生成文档,在文档的开头使用
3.3 数据类型
Java是强类型语言,所有变量必须声明一种类型
基本类型一共8种
整形4种: int(4) short(2) long(8) byte(1)
浮点型2种: float(4) double(8)
char型: char(一个代码单元)
boolean型: boolean(true or false)
3.3.1 整型
长整型后缀 L或l
十六进制前缀 0x或0X
八进制前缀 0
二进制前缀 0b或0B
可以在数字字面量上加下划线以便阅读,如1_000_000或0b1111_0100_0100_0000
- Java没有无符号型
3.3.2 浮点型
float类型后缀是F或f
不加默认是double型
double型也可以加D或d
表示溢出和出错有种特殊的浮点数值
- 正无穷 如正整数除以0 用常量Double.POSITIVE_INFINITY表示
- 负无穷 用常量Double.NEGATIVE_INFINITY表示
- NaN 不是个数 如0/0 用Double.NaN表示
- 不能用等式比较一个数是否等于Double.NaN,但可以用Double.isNaN()方法
3.3.3 char类型
描述UTF-16编码中的一个代码单元,长度为16位
字面量用单引号’ ‘括起来
也可以用转义字符\u或\U表示 范围\u0000~\uFFFF
此转义字符可以出现在单双引号外面,而其他转义字符,如\n \t等不可以
- \u或\U开头的转义字符会在代码解析前得到处理,例如双引号”和\u0022的效果一样,这就是它能出现在引号外面的原因
- 在注释中如果不小心写了\u,很可能会酿成错误!例如\u00A0是换行符,写了之后等同于换行;\users也是错的,因为\u后面没有跟着四个十六进制数,好麻烦!!!
3.3.4 Unicode和char类型
简言之,UTF-16编码中,基本的多语言级别中的字符用一个代码单元表示,占16位,其码点取值范围与辅助字符的任何码点都不交叉;辅助字符用存储上连续的两个代码单元表示,前一个代码单元码点值为U+D800~U+DBFF,后一个代码单元码点值为U+DC00~U+DFFF。这样就能从代码单元码点值看出其属于字符的编码还是辅助字符的第一或第二个部分。
3.3.5 boolean类型
值有true和false两个类型
- 与C++不同,其值不能与整形互相转换!!!
3.4 变量
变量以字母开头,由数字、字母构成,长度不限。(字母可是Unicode字符集中的任意字母,中文当然不算了~)
3.4.1 变量初始化
变量声明后,必须显示初始化,否则之间使用会出错
可以把声明和初始化放在一行,也可以分开,都行,就是别忘了初始化,否则编译器会报错
- 与C++不同,Java不区分声明和定义,那么问题来了,它怎么知道我是声明还是定义呢?
3.4.2 常量
关键字final指示常量
例如
final double CM_PER_INCH = 2.54;
表示该变量只能被赋值一次,之后不能更改
可以在一个类的内部定义内常量,如
public static final double CM_PER_INCH = 2.54;
那么它可以被类中其它方法使用,如果其为public属性,其它类也可以使用它
- 与C++不同,const关键字目前还未在Java中使用
3.5 运算符
运算符基本上和C++一样
double类型使用64位存储,但有些机器寄存器80位,Java在默认情况下,中间计算结果使用扩展精度,因此不同机器计算结果可能不一样。但是如果需要一样的话,就在方法前加关键字strictfp,这样计算过程中都是按照64位进行运算,到任何机器上结果都一样。
例如:
public static strictfp void main(String[] args)
3.5.1 数学函数与常量
有个类名为Math,里面有很多常用的数学函数,如
- Math.sqrt(x)
- Math.pow(x, a)
- Math.floorMod()
- Math.sin Math.cos Math.tan Math.atan Math.atan2等三角函数
- Math.exp Math.log Math.log10
- Math.round 对浮点型舍入运算 返回long?
以及两常量的近似值 Math.PI Math.E
如果不想在每个方法前加类名,那么可以在源文件顶部加
import static java.lang.Math.*;
如果要求运算结果完全可预测,应该使用StrictMath类,其运算速度应该会慢一些
3.5.2 数字类型之间的转换
对于转换,有些是有精度损失的,有些则没有
四种整型数据由低到高无损失
浮点型由float到double无损失
char到int无损失
int到float有损失 int到double无损失
long到double有损失
其他的可以根据这个原则推算出来
二元操作时,按照两个操作数的类型
double>float>long>int
如果有前面的类型,两个操作数就都转为前面的同一种类型,否则都是int
3.5.3 强制类型转换
强制类型转换同C++
不要强制转换boolean型数据,如果需要,使用三目运算符:x ? 1 : 0
3.5.4结合赋值和运算符
+= -=等
- 如果x是int型, x+=3.5相当于(int)(x+3.5),厉害吧!
3.5.5 自增自减运算符
同C++
3.5.6 关系和boolean运算符
同C++
就是 == !=那些
以及与&& 或|| 非!
3.5.7 位运算符
& | ^ ~
同C++
>>算术右移,用符号位填充高位,与C++不同
>>>逻辑右移,用0填充高位,
<<左移
- 值得注意,左移右移位数不会超过左操作数实际的长度,如果超过,就会将右操作数取模,最终不超过那么多位
3.5.8 括号与运算符级别
除了没有逗号运算符,其他同C++
3.5.9 枚举类型
enum Size {SMALL, MEDIUM, LARGE};
有待介绍
3.6 字符串
类String,字符串是其实例,如
String e = “”;
3.6.1 子串
s.substring(a,b)方法可提取子串,a是起始位,b是第一个不想的字符位置
3.6.2 拼接
- 使用加号+可以拼接字符串,中间不带空格
- 加号的与非字符串的值拼接时(不分先后),后者转化为字符串,任何Java对象都可以转化为字符串。
- String.join(“xx”, “s”, “a”)方法可以拼接多个字符串,第一个是定界符,插在字符串中间。
3.6.3 不可变字符串
String类不能修改字符串,但是可以修改字符串变量对应的值
3.6.4 检测字符串是否相等
- s.equals(t)方法,当相等时返回true
- s.equalsIgnoreCase(t)方法,忽略大小写
- 不能使用==检测字符串是否相等!!!
3.6.5 空串与null串
- 空串”“有内容(空)和长度(0),检测空串用
if(str.length() == 0) 或 if(str.equals(“”)) - String类变量可以存放特殊值null,此时无对象与其关联,检测用
if(str == null) 这时可以用等号
3.6.6 码点与代码单元
一个码点表示一个字符,其可能占一个代码单元,也可能占两个代码单元
char类型占一个代码单元,所以导致混乱,具体看书P49
- 不建议使用char类型,因为太底层,那我该用什么?
3.6.7 String API
java.lang是包名
java.lang.String表示String类
这里汇总了一些常用的方法
3.6.8 阅读联机文档
3.6.9 构建字符串
当需要连续输入或拼接字符串时,采用字符串连接的形式浪费空间和时间,所以可以用StringBuilder类,如
StringBuilder builder = new StringBuilder();
builder.append(ch);
builder.append(str);
String strr = builder.toString();
- 还有一些其他的方法在书上
- 如果是多线程,可以用StringBuffer类,API相同,效率稍低
3.7 输入输出
3.7.1 读取输入
首先构造一个Scanner对象,并与System.in关联
Scanner in = new Scanner(System.in)然后使用各种API读取数据
String name = in.nextLine();读取一行,包括空格
String firstName = in.next();读取一个单词,以空格分割
int age = in.nextInt();读取一个整数
等等等使用Scanner类必须import包java.util.*
凡是没有定义在java.lang包中的类都要import
3.7.2 格式化输出
- 可以用System.out.print(x),能够自动根据格式调整输出
- 也可以使用System.out.printf(),同C语言,只不过加了一些转换符,可以显示时间等,还可以在输出时使用标志,以更方便阅读
- 可以使用静态方法String.format创建格式化字符串,而不用输出
3.7.3 文件输入与输出
- 读取文件
用File对象构造一个Scanner对象
Scanner in = new Scanner(Path.get(“myfile.txt”),”UTF-8”); - 写入文件
只提供文件名,构造PrintWriter对象
PrintWriter out = new PrintWriter(“myfile.txt”, “UTF-8”);
不存在则新建文件 - 如果已知可能会出现输入输出异常,用throws子句标记main方法
… main(…) throws IOException
3.8 控制流程
多数与C++一样
3.8.1 块作用域
不同之处在于不能在嵌套的块中定义与外层块相同名称的变量,而C/C++可以
3.8.2 条件语句
if else
同C/C++
3.8.3 循环
while do while
同C/C++
3.8.4 确定循环
for循环
同C/C++
3.8.5 多重选择:switch语句
语法一样
case标签可以是:
- 类型为char、byte、short或int的常量表达式(long不行?)
- 枚举常量 不用指定枚举类型名,直接使用即可
- 字符串字面量
3.8.6 中断控制流程语句
break continue基本用法同C++
多两种:
- 带标签的break,把标签放在要跳出的块前,紧跟冒号。用法是break label_name; 当有多层循环嵌套时可用,也可用于任何语句中。
- 带标签的continue,跳到与标签匹配的循环首部。我理解是当有多层循环嵌套时使用。
3.9 大数值
当基本整数与浮点数运算精度不满足时使用额外的两个类,实现任意长度和精度的运算
- BigInteger整数
- BigDecimal浮点数
valueOf方法可将普通数值转成大数值
常用的加减乘除只能使用类中的方法,不能使用符号,因为Java不提供运算符重载功能
3.10 数组
与C++不同之处较多
数组声明有两种,均与C++不同
- int[] a
- int a[]
如此未经初始化,不可直接使用
也可以定义时初始化
- int[] a = new int[100];
或者定义后初始化
- a = new int[100]
初始化时数字数组初始化为0,boolean数组初始化为false,对象数组初始化为null
- 超出下标区域将报错
- a.length方法返回数组长度
- 一旦创建,大小不可改变
3.10.1 for each循环
类似于python
for(变量 : 集合) {}
遍历数组比较方便,不需要知道数组长度
3.10.2 数组初始化以及匿名数组
- 可以像C++一样
int[] a ={2, 3, 4, 5}; - 可以创建匿名数组
a = new int[] {17, 18, 19, 20}
用于在不创建新变量的情况下重新初始化数组,数组长度可以修改? - 数组长度可以是0,与null不同
int[] a = new int[0];
3.10.3 数组拷贝
int[] a = b;
a[5] = 12
那么b[5]也成了12,本质上两个变量引用同一数组,和C++引用传递差不多
也可以使用Arrays类的copyOf方法,这样就创建了新数组:
int[] a = Arrays.copyOf(b, length);
如果length小于数组长度将只拷贝前面的元素
如果length大于数组长度将在后面补充0
3.10.4 命令行参数
就是main函数的形参String[] args
- 与C++不同,其内容不包括程序名
3.10.5 数组排序
用Array.sort方法
还有一些其他方法
3.10.6 多维数组
定义类似于:
double[][] a;
初始化类似上面的
- for each不能自动处理二维数组,需要两层,如
for(double[] row : a)
for(double value : row)
{}
3.10.7 不规则数组
与C++不同,java的多维数组真的是数组的数组,因此可以:
- 直接互换两行
double[] temp = a[i];
a[i] = a[j];
a[j] = temp; - 每行长度可不同
需要先分配行数
int[][] odds = new int[MAX+1][];
然后逐行分配
for(int n=0; n<=MAX;n++)
odds[n] = new int[n+1];
完结撒花