Java的基本程序设计结构(一)
1、一个简单的Java应用程序
1 public class FirstSample { 2 public static void main(String[] args) { 3 System.out.println("We will not use 'Hello, World!'"); 4 } 5 }
(1)Java区分大小写,如果出现了大小写拼写错误(例如,将main拼写成Main),程序将无法运行。
(2)关键字public称为访问修饰符,用于控制程序的其他部分对这段代码的访问级别。
(3)关键字class后面紧跟类名。Java中定义雷鸣的规则很宽松。名字必须以字母开头,后面可以跟字母和数字的任意组合。长度基本上没有限制。但是不能使用Java保留字(例如,public或class)作为类名。
标准的命名规范(类名FirstSample就遵循了这个规范):类名是以大写字母开头的名词。如果名字由多个单词组成,每个单词的第一个字母都应该大写。
(4)源代码的文件名必须与公共类的名字相同,并用 .java 作为扩展名。
注意:一个 .java 文件中可以存在多个类,但只能有一个公共类。
2、数据类型
在Java中,一共有8种基本类型(primitive type),其中有4种整型,2种浮点类型、1种用于表示Unicode编码的字符单元的字符类型char和一种用于表示真值的boolean类型。
(1)整型
① 在通常情况下,int类型最常用。但如果表示星球上的居住人数,就需要使用long类型了。byte和short类型主要用于特定的应用场合,例如,底层的文件处理或者需要控制占用存储空间量的大数组。
②在Java中,整型的范围与运行Java代码的机器无关。这就解决了软件从一个平台移植到另一个平台,或者在同一个平台中的不同操作系统之间进行移植给程序员带来的诸多问题。
③长整型数值有一个后缀 L 或 l (如4000000000L)。十六进制数值有一个前缀 0x 或 0X (如 0xCAFE)。八进制有一个前缀 0 ,例如,010对应八进制中的8。从Java7开始,加上前缀 0b 或 0B 就可以写二进制数。例如,0b1001 就是 9。
(2)浮点类型
浮点类型用于表示有小数部分的数值。在Java中有两种浮点类型,具体内容如下表:
double 表示这种类型的数值精度是 float 类型的两倍(有人称之为双精度数值)。绝大部分应用程序都采用 double 类型。在很多情况下,float 类型的精度很难满足需求。实际上,只有很少的情况适合使用 float 类型,例如,需要单精度数据的库,或者需要存储大量数据。
float 类型的数值有一个后缀 F 或 f (例如,3.14F)。没有后缀 F 的浮点数值(如3.14)默认为 double 类型。当然,也可以在浮点数值后面添加或缀 D 或 D。
注:浮点数值不适用于无法接受舍入误差的金融计算中。例如,命令System.out.println(2.0-1.1)将打印出0.8999999999999999,而不是人们想象中的0.9。如果在数值计算中不允许有任何舍入误差,就应该是用BigDecimal类。
(3)char类型
char类型的字面量值要用单引号括起来。例如,'A'是编码值为65所对应的字符常量,它与“A”不同,"A"是包含一个字符A的字符串。char类型的值可以表示为十六进制值,其范围从 \u0000 到 \Uffff。
(4)boolean类型
boolean(布尔)类型有两个值:false和true,用来判定逻辑条件。整型值和布尔值之间不能进行相互转换。
3、变量
① 在Java中,每个变量都有一个类型(type)。在声明变量时,变量的类型位于变量名之前。eg:
double salary;
int vacationDays;
long earthPopulation;
boolean done;
② 每个声明以分号结束。由于声明是一条完整的Java语句,所以必须以分号结束。
③ 变量名必须是一个以字母开头并由字母或数字构成的序列。变量名中所有的字符都是有意义的,并且大小写敏感( hireday 和 hireDay 是两个不同的变量名)。变量名的长度基本上没有限制。
提示:尽管 $ 是一个合法的Java字符,但不要在自己的代码中使用这个字符。它只用在Java编译器或其他工具生成的名字中。
④不能使用Java保留字作为变量名。
⑤可以在一行中声明多个变量:int i, j; 不过,不提倡使用这种风格。逐一声明每一个变量可以提高程序的可读性。
(1)变量初始化
声明一个变量之后,必须用赋值语句对变量进行显式初始化,千万不要使用未初始化的变量。例如,Java编译器认为下面的语句序列是错误的:
1 int vacationDays; 2 System.out.println(vacationDays);
要想对一个已经声明过的变量进行赋值,就需要将变量名放在等号(=)左侧,相应取值的 Java 表达式放在等号的右侧。
int vacationDays;
vacationDays = 12;
也可以将变量的声明和初始化放在同一行中。例如:int vacationDays = 12;
在Java中,变量的声明尽可能地靠近变量第一次使用的地方。
(2)常量
在Java中,利用关键字 final 指示常量。例如:
1 public class Constants { 2 public static void main(String[] args) { 3 final double CM_PER_INCH = 2.54; 4 double paperWidth = 8.5; 5 double paperHeight = 11; 6 System.out.println("Paper size in centimeters:" 7 + paperWidth * CM_PER_INCH + " by " + paperHeight * CM_PER_INCH ); 8 } 9 }
关键字 final 表示这个变量只能被赋值一次。一旦被赋值之后,就不能再更改了。习惯上,常量名使用全大写。
在Java中,经常希望某个常量可以在一个类中的多个方法中使用,通常将这些常量称为类常量。可以使用关键字 static final 设置一个类常量。eg:
1 public class Constants { 2 public static final double CM_PER_INCH = 2.54; 3 public static void main(String[] args) { 4 double paperWidth = 8.5; 5 double paperHeight = 11; 6 System.out.println("Paper size in centimeters:" 7 + paperWidth * CM_PER_INCH + " by " + paperHeight * CM_PER_INCH ); 8 } 9 }
4、运算符
需要注意,整数被0除将会产生一个异常,而浮点数被0除将会得到无穷大或 NaN 结果。
(1)数学函数与常量
在Math类中,包含了各种各样的数学函数。
① 计算一个数值的平方根,可以使用 sqrt 方法。eg:
1 double x = 4; 2 double y = Math.sqrt(x); 3 System.out.println(y);
② 幂运算,Math类中 pow 方法。语句:double y = Math.pow(x, a); 将 y 的值设置为 x 的 a 次幂。pow 方法有两个 double 类型的参数,其返回结果也为 double 类型。
③ 常用的三角函数
Math.sin
Math.cos
Math.tan
Math.atan
Math.atan2
④ 指数函数以及它的反函数——自然对数以及以10为底的对数
Math.exp
Math.log
Math.log10
⑤ Java还提供了两个用于表示 π 和 e 常量的近似值:Math.PI、Math.E
(2)数值类型之间的转换
经常需要将一种数值类型转换为另一种数值类型。下图给出了数值类型之间的合法转换。
上图中有 6 个实心箭头,表示无信息丢失的转换;有 3 个虚箭头,表示可能有精度损失的转换。
当时用不同类型的两个数值进行二元操作时(例如 n + f ,n 是整数,f 是浮点数),先要将两个操作数转换为同一种类型,然后再进行计算。
- 如果两个操作数中有一个是 double 类型,另一个操作数就会转换为 double 类型。
- 否则,如果其中一个操作数是 float 类型,另一个操作数就会转换为 float 类型。
- 否则,如果其中一个操作数是 long 类型,另一个操作数将会转换为 long 类型。
- 否则,两个操作数都将被转换为 int 类型。
(3)强制类型转换
强制类型转换的语法格式是在圆括号中给出想要转换的目标类型,后面紧跟待转换的变量名。例如:
1 double x = 9.997; 2 int nx = (int) x;
这样,变量 nx 的值为 9 。强制类型转换通过截断小数部分将浮点值转换为整型。
如果想对浮点数进行舍入运算,以便得到最接近的整数,那就需要使用 Math.round 方法:
1 double x = 9.997; 2 int nx = (int) Math.round(x);
现在,变量 nx 的值为10。当调用 round 的时候,仍然需要使用强制类型转换 (int)。其原因是 round 方法返回的结果为 long 类型,由于存在信息丢失的可能性,所以只有使用显式的强制类型转换才能够将 long 类型转换为 int 类型。
(4)运算符
运算符分类
① 算术运算符
算术运算符中+,-,*,/,%属于二元运算符,二元运算符指的是需要两个操作数才能完成运算的运算符。其中的%是取模运算符,就是我们常说的求余数操作。
二元运算符的运算规则:
整数运算:
1. 如果两个操作数有一个为Long, 则结果也为long。
2. 没有long时,结果为int。即使操作数全为short,byte,结果也是int。
浮点运算:
3. 如果两个操作数有一个为double,则结果为double。
4. 只有两个操作数都是float,则结果才为float。
取模运算:
1.其操作数可以为浮点数,一般使用整数,结果是“余数”,“余数”符号和左边操作数相同,如:7%3=1,-7%3=-1,7%-3=1。
算术运算符中++,--属于一元运算符,该类运算符只需要一个操作数。
② 赋值及其扩展赋值运算符
注:如果运算符得到一个值,其类型与左侧操作数的类型不同,就会发生强制类型转换。例如,如果 x 是一个 int ,则以下语句 x += 3.5; 是合法的,将把 x 设置为 (int)(x + 3.5)。
③ 自增与自减运算符
有两种形式:
前缀: ++n 或 --n
后缀: n++ 或 n--
后缀和前缀形式都会是变量值加 1 或减 1。但用在表达式中而这就有区别了。前缀形式会先完成加 1;而后缀形式会使用变量原来的值。
1 int m = 7; 2 int n = 7; 3 int a = 2 * ++m; // 现在 a = 16, m = 8 4 int b = 2 * n++; // 现在 b = 14, n = 8
建议不要在表达式中使用 ++。
④ 关系运算符
注意事项
-
=是赋值运算符,而真正的判断两个操作数是否相等的运算符是==。
-
==、!= 是所有(基本和引用)数据类型都可以使用
-
> 、>=、 <、 <= 仅针对数值类型(byte/short/int/long, float/double。以及char)
⑤ 逻辑运算符
&& 和 || 运算符是按照“短路”方式来求值的:如果第一个操作数已经能够确定表达式旳值,第二个操作数就不必计算了。如果用 && 运算符合并两个表达式,
expression1 && expression2
而且已经计算得到第一个表达式旳真值为 false ,那么结果就不可能为 true 。因此,第二个表达式就不必计算了。可以利用这一点来避免错误。例如,在下面的表达式中:
x != 0 && 1 / x > x + y;
如果 x 等于 0,那么第二部分就不会计算。因此,如果 x 为 0,也就不会计算 1 / x ,除以 0 的错误就不会出现。
类似地,如果第一个表达式为 true, expression1 || expression2 的值就自动为 true,而无需计算第二个表达式。
⑥ 位运算符
雷区
1. &和|既是逻辑运算符,也是位运算符。如果两侧操作数都是boolean类型,就作为逻辑运算符。如果两侧的操作数是整数类型,就是位运算符。
2. 不要把“^”当做数学运算“乘方”,是“位的异或”操作。
⑦ 条件运算符
语法格式:x ? y : z
其中 x 为 boolean 类型表达式,先计算 x 的值,若为true,则整个运算的结果为表达式 y 的值,否则整个运算结果为表达式 z 的值。
⑧ 运算符优先级
建议
-
不需要去刻意的记这些优先级,表达式里面优先使用小括号来组织!!
-
逻辑与、逻辑或、逻辑非的优先级一定要熟悉!(逻辑非>逻辑与>逻辑或)。如:
-
a||b&&c的运算结果是:a||(b&&c),而不是(a||b)&&c
(5)枚举类型
有时候,变量的取值只在一个有限的集合内。例如:销售的服装或比萨饼只有小、中、大和超大这四种尺寸。当然,可以将这些尺寸分别编码为 1、2、3、4 或 S、M、L、X。但这样存在着一定的隐患。在变量中很可能保存的是一个错误的值(如 0 或 m)。
针对这种情况,可以自定义枚举类型。枚举类型包括有限个命名的值。例如,
enum Size {SMALL, MEDIUM, LARGE,EXTRA_LARGE};
现在,可以声明这种类型的变量:Size s = Size.MEDIUM;
Size 类型的变量只能存储这个类型声明中给定的某个枚举值,或者 null 值,null 表示这个变量没有设置任何值。
5、字符串
Java没有内置的字符串类型,而是在标准 Java 类库中提供了一个预定义类,很自然地叫做 String 。每个用双引号括起来的字符串都是 String 类的一个实例:
1 String e = ""; // 一个空字符串 2 String greeting = "Hello";
(1)子串
String 类的 substring 方法可以从一个较大的字符串提取出一个子串。例如:
1 String greeting = "Hello"; 2 String s = greeting.substring(0, 3);
创建了一个由字符 “Hel” 组成的字符串。
substring 方法的第二个参数是不想复制的第一个位置。这里要复制位置为 0、1 和 2(从 0 到 2,包括 0 和 2)的字符。在 substring 中从 0 开始计数,直到 3 为止,但不包含 3。
substring 工作方式有一个优点:容易计算子串的长度。字符串 s.substring(a, b) 的长度为 b - a。例如,子串 “Hel” 的长度为 3 - 0 = 3。
(2)拼接
① 与绝大多数的程序设计语言一样, Java 语言允许使用 + 号连接(拼接)两个字符串。
② 当将一个字符串与一个非字符串的值进行拼接时,后者被转换成字符串。例如:
int age = 13; String rating = "PG" + age;
rating 设置为 “PG13”。
③ 如果需要把多个字符串放在一起,用一个定界符分隔,可以使用静态 join 方法:
1 String all = String.join(" / ", "S", "M", "L", "XL"); // 字符串 all 为 "S / M / L / XL"
(3)不可变字符串
String 类没有提供用于修改字符串的方法。如果希望将 greeting 的内容修改为 “Help!”,不能直接将 greeting 的最后两个未知的字符修改为 ‘p’ 和 ‘!’ 。如何修改这个字符串呢?首先提取需要的字符,然后再拼接上替换的字符串:
1 greeting = greeting.substring(0, 3) + "p!";
上面这条语句将 greeting 当前值修改为 “Help!”。
由于不能修改 Java 字符串中的字符,所以在 Java 文档中将 String 类对象称为不可变字符串,如同数字 3 永远是数字 3 一样,字符串 “Hello” 永远包含字符 H、e、l、l 和 o 的代码单元序列,而不能修改其中的任何一个字符。当然,可以修改字符串变量 greeting,让它引用另外一个字符串,这就如同可以将存放 3 的数值变量改成存放 4 一样。
不可变字符串有一个优点:编译器可以让字符串共享。可以想象将各种字符串存放在公共的存储池中。字符串变量指向存储池中相应的位置。如果复制一个字符串变量,原始字符串与复制的字符串共享相同的字符。
如果,将 greeting 赋予另外一个值又会怎样呢?
1 greeting = "Howdy";
这样做会不会产生内存泄漏呢?毕竟,原始字符串放置在堆中。十分幸运,Java将自动地进行垃圾回收。如果一块内存不再使用了,系统最终会将其回收。
(4)检测字符串是否相等
① 可以使用 equals 方法检测两个字符串是否相等。对于表达式:
1 s.equals(t)
如果字符串 s 与 字符串 t 相等,则返回 true ;否则,返回 false。需要注意的是, s 与 t 可以是字符串变量,也可以是字符串字面量。
② 要想检测两个字符串是否相等,而不区分大小写,可以使用 equalsIgnoreCase 方法。
1 "Hello".equalsIgnoreCase("hello");
③ 一定不要使用 == 运算符检测两个字符串是否相等!这个运算符只能够确定两个字符串是否放置在同一个位置上(判断两个字符串的存储地址是否相等)。当然,如果字符串放置在同一个位置上,它们必然相等。但是,完全有可能将内容相同的多个字符串的拷贝位置放置在不同的位置上。
1 String greeting = "Hello"; 2 if(greeting == "Hello") ... 3 // 可能为true 4 if(greeting.substring(0, 3) == "Hel") ... 5 // 可能为false
如果虚拟机始终将相同的字符串共享,就可以使用 == 运算符检测是否相等。但实际上只有字符串常量是共享的,而 + 或 substring 等操作产生的结果并不是共享的。因此,千万不要使用 == 运算符测试字符串的相等性,以免在程序中出现糟糕的 bug。
(5)空串与 null 串
空串 "" 是长度为 0 的字符串。可以调用以下代码检查一下字符串是否为空:
if(str.length == 0) 或 if(str.equals(""))
空串是一个 Java 对象,有自己的串长度(0)和内容(空)。不过,String 变量还可以存放一个特殊的值,名为 null,这表示目前没有任何对象与该变量关联。要检查一个字符串是否为 null,要是用以下条件:
if(str == null)
有时要检查一个字符串既不是 null 也不为空串,这种情况下就需要使用以下条件:
if(str != null && str.lengtj() != 0)
(6)构建字符串
有的时候,需要由较短的字符串构建字符串,例如,按键或来自文件中的单词。采用字符串连接的方式达到此目的效率比较低。每次连接字符串,都会构建一个新的 String 对象,既耗时,又浪费空间。使用 StringBuilder 类就可以避免这个问题的发生。
如果需要用许多小段的字符串构建一个字符串,那么应该按照下列步骤进行。
① 首先,构建一个空的字符串构建器:
1 StringBuilder builder = new StringBuilder();
② 当每次需要添加一部分内容时,就调用 append 方法。
1 builder.append(ch); // 增加单个字符 2 builder.append(str); // 增肌一个字符串
③ 在需要构建字符串时就调用 toString 方法,将可以得到一个 String 对象,其中包含了构建器中的字符序列。
1 String completeString = builder.toString();
注:在 JDK5.0 中引入 StringBuilder 类。这个累的前身是 StringBuffer,其效率稍有些低,但允许采用多线程的方式执行添加或删除字符的操作。如果所有字符串在一个单线程中编辑,则应该使用 StringBuilder 替代它。这两个类的 API 是相通的。
(7)大数值
如果基本的整数和浮点数精度不能够满足需求,那么可以使用 java.math 包中的两个很有用的类: BigInteger 和 BigDecimal 。这两个类可以处理包含任意长度数字序列的数值。BigInteger 类实现了任意精度的整数运算,BigDecimal 实现了任意精度的浮点数运算。
使用静态的 valueOf 方法可以将普通的数值转换为大数值:
1 BigInteger a = BigInteger.valueOf(100);
遗憾的是,不能使用人们熟悉的算数运算符(如:+ 和 *)处理大数值。而需要使用大数值类中的 add 和 multiply 方法。
1 BigInteger c = a.add(b); // c = a + b; 2 BigInteger d = c.multiply(b.add(BigInteger.valueOf(2))); // d = c * (b + 2)
API java.math.BigInteger 1.1
1 BigInteger add(BigInteger other) 2 BigInteger substract(BigInteger other) 3 BigInteger multiply(BigInteger other) 4 BigInteger divide(BigInteger other) 5 BigInteger mod(BigInteger other)
返回这个大整数和另一个大整数 other 的和、差、积、商以及余数。
1 int compareTo(BigInteger other)
如果这个大整数与另一个大整数 other 相等,返回 0;如果这个大整数小于另一个大整数 other,返回负数;否则,返回正数。
1 static BigInteger valueOf(long x)
返回值等于 x 的大整数。
java.math.BigDecimal 1.1
1 BigDecimal add(BigDecimal other) 2 BigDecimal substract(BigDecimal other) 3 BigDecimal multiply(BigDecimal other) 4 BigDecimal divide(BigDecimal other) 5 BigDecimal mod(BigDecimal other, RoundingMode mode)
返回这个大实数与另一个大实数 other 的和、差、积、商。要想计算商,必须给出舍入方式(rounding mode)。RoundingMode.HELP_UP 是在学校中学习的四舍五入方式。
1 int compareTo(BigDecimal other)
如果这个大实数与另一个大实数 other 相等,返回 0;如果这个大实数小于另一个大实数 other,返回负数;否则,返回正数。
static BigDecimal valueOf(long x) static BigDecimal valueOf(long x, int scale)
返回值为 x 或 x/10^scale 的一个大实数。