Java的基本程序设计结构
Java中,长整型数值有一个后缀L或l(如400000000L)。十六进制数值有一个前缀0x或0X(如0xCAFE)。八进制有一个前缀0,比如010对应十进制中的8。建议最好不要使用八进制常数。
从Java7开始,加上前缀0b或0B就可以写二进制数。比如0b1001就是9。还可以为数字加上下划线,比如1_000_000表示100万,只是为了让人更易读。Java编译器会去除这些下划线。
浮点值不适用于无法接受舍入误差的金融计算。比如2.0-1.1输出的是0.8999999999,而不是0.9。这种误差的原因是浮点数值采用二进制系统表示,而在二进制系统中无法精确地表示分数1/10。
类常量
在Java中,经常希望某个常量可以在一个类的多个方法中使用,通常将这些常量称为类常量,可以使用关键字static final设置一个类常量。类常量的定义位于main方法的外部。
关键词final表示这个变量只能被赋值一次。一旦被赋值后就不能够再更改了。习惯上常量名使用全大写。
public class HelloJava {
public static final double PI=3.14;//定义一个类常量
public static void main(String[] args) {
double width=8.5;
System.out.print(PI*width);
}
}
二元运算符数值类型转换
当使用二元运算符连接两个值时(例如n+f),要先将两个操作数转换为同一种类型,然后进行计算。
如果两个操作数中有一个是double类型,另一个操作数就会转换为double类型。
如果两个操作数中有一个是float类型,另一个操作数就会转换为float类型。
如果两个操作数中有一个是long类型,另一个操作数就会转换为long类型。
否则,两个操作数都将转换为int类型。
如果第一个操作数已经能够确定表达式的值,第二个操作数就不必计算了。(比如A&&B,如果A为假,那整个式子一定为假,B就不用计算了。同理,A||B,如果A为真,那也不用计算B了,因为式子一定为真)
将一个字符串与一个非字符串的值进行拼接时,后者会转换成字符串。
public class HelloJava {
public static void main(String[] args) {
int age=13;
String rating="PG"+age;//age转化成字符串
System.out.print(rating);
}
}
码点与代码单元
char数据类型是一个采用UTF-16编码表示Unicode码点的代码单元。最常用的Unicode字符使用一个代码单元就可以表示(a,b,c等),而辅助字符(𝕆等)需要两个代码单元表示。
码点:是指一个编码表中的某个字符对应的代码值。(也就是字符在Unicode表中的位置)
在Java中一个Unicode占2个字节(byte),一个字节等于8比特位(bit),因此,每个Unicode码占用16个比特位。若一个字符的代码长度为16位,则为一个代码单元。若一个字符的代码长度有两个16的代码长度编码,则该字符有两个代码单元。
获取方法:
String.length():返回字符串代码单元的个数。
String.codePointCount(int startIndex,int endIndex):返回startInde到endIndex-1之间的码点个数。(也就是有多少个字符,因为一个字符对应一个位置(代码值))
String.charAt(int index):返回给定位置的代码单元,尽可能不要调用这个方法。
String.codePointAt(int index):返回给定位置的码点(也就是该字符在Unicode中的位置)
String.offsetByCodePoints(int startIndex,int cpCount):返回从stratIndex码点开始,cpCount个码点后的码点索引。
举例:hi𝕆 这个字符串实际上有三个码点,四个代码单元。
1.测试length函数和codePointCount函数
public class HelloJava {
public static void main(String[] args) {
String greeting="hi𝕆";
int t=greeting.length();
int h=greeting.codePointCount(0, t);
System.out.println(t);
System.out.println(h);
}
}
首先测试的是测量字符串长度的两种方法——用length方法得出字符串代码单元个数为4,因为𝕆这个符号是由两个代码单元得到的。而用count方法得出字符串码点个数为3,也就是有多少个字符。
2.测试charAt函数
public class HelloJava {
public static void main(String[] args) {
String greeting="hi𝕆";
char a=greeting.charAt(0);
char b=greeting.charAt(1);
char c=greeting.charAt(2);
char d=greeting.charAt(3);
System.out.println(a);
System.out.println(b);
System.out.println(c);
System.out.println(d);
}
}
由于charAt函数是返回给定位置的代码单元,因此在第2个和第三个位置返回的是相同的代码单元。charAt可以在没有辅助元素的字符串中使用。
3.测试codePointAt函数
public class HelloJava {
public static void main(String[] args) {
String greeting="hi𝕆";
int a=greeting.codePointAt(0);
int b=greeting.codePointAt(1);
int c=greeting.codePointAt(2);
int d=greeting.codePointAt(3);
System.out.println(a);
System.out.println(b);
System.out.println(c);
System.out.println(d);
}
}
codePointAt函数返回的是序号对应的字符在Unicode表中的位置,可以看出h和i位置是挨在一起的,并且𝕆符号是由两个不同代码单元组成的。
4.测试offsetByCodePoints函数
public class HelloJava {
public static void main(String[] args) {
String greeting="hi𝕆";
int a=greeting.offsetByCodePoints(0, 0);
int b=greeting.offsetByCodePoints(0, 1);
int c=greeting.offsetByCodePoints(0, 2);
System.out.println(a);
System.out.println(b);
System.out.println(c);
}
}
经过的是码点数而不是代码单元,因此这个就可以看作是从序号为startIndex开始,经过cpCount个位置,对应的码点的序号。与代码单元就没有关系了。
总结:
String.codePointCount(int startIndex,int endIndex) 是最标准的求字符串长度的函数
int cpCount = String.codePointCount(0,String.length());
String.codePointAt(int index) 是最标准的求码点在Unicode位置的函数
String.offsetByCodePoints(int startIndex,int cpCount) 是最标准的求码点对应序号的函数
想要得到第i个码点,应该使用下列语句
int index = String.offsetByCodePoints(0,i);
int cp = String.codePointAt(index);
构建字符串
当需要用字符串构建字符串的时候,使用拼接效率会很低,每次拼接字符串时,都会构建一个String对象,既耗时,又浪费空间。因此使用StringBuilder类
构建一个空的字符串构建器:
StringBuilder builder=new StringBuilder();
其中builder是构建器的名字。
当每次需要添加一部分时,就调用append方法:
builder.append("Hello,");
builder.append("World!");
在字符串构建完成后,调用toString方法,得到一个String对象:
String s=builder.toString();
public class HelloJava {
public static void main(String[] args) {
StringBuilder builder=new StringBuilder();
builder.append("Hello,");
builder.append("World!");
String s=builder.toString();
System.out.print(s);
}
}
Java的输入
输出很简单,使用System.out.println即可,但是要想通过控制台进行输入,则需要构造一个与“标准输入流” System.in 关联的Scanner对象。构造之后,就可以使用Scanner类的各种方法读取输入了。
import java.util.*;
public class HelloJava {
public static void main(String[] args) {
Scanner in =new Scanner(System.in);//构造Scanne对象
System.out.print("What is your name?");
String name=in.nextLine();//读取一行输入
System.out.println("Hello, "+name);
System.out.print("How old are you?");//读取一个整数
int age=in.nextInt();
System.out.println(age);
}
}
当使用的类不是定义在基本包java.lang中时,一定要使用import导入相应的包。
在标准化输出中,System.out.printf也可以用C语言的语法。
import java.util.*;
public class HelloJava {
public static void main(String[] args) {
Scanner in =new Scanner(System.in);//构造Scanne对象
System.out.print("What is your name?");
String name=in.nextLine();//读取一行输入
System.out.printf("你的名字是%s",name);
}
}
不能在嵌套的两个块中声明同名的变量。(在C++中,可以在嵌套的块中重定义一个变量。在内层定义的变量会覆盖在外层定义的变量。但Java不允许这样做)
带标签的break
虽然在Java中并不会使用goto语句,但是设计者还是增加了一个与goto类似的语法——带标签的break,用于跳出多重嵌套的循环语句。标签必须放在希望跳出的最外层循环之外,并且必须紧跟一个冒号。
public class HelloJava {
public static void main(String[] args) {
int n=10,i=0;
read_data://标签
while(i<n) {
for(int j=0;j<20;j++) {
i++;
if(i==8)
break read_data;//标签break的语法
}
i++;
}
System.out.print(i);
}
}
如果没有提前退出,i的结果应该是10,而现在为8。
同理,continue也有带标签的continue,语法与break相同。
大数
java.math包中有两个很有用的类:BigInteger和BighDecimal。这两个类可以处理包含任意长度数字序列的数值。BigInteger类实现任意精度的整数运算,BigDecimal实现任意精度的浮点数运算。
使用valueOf方法可以将普通的数值转换为大数
BigInteger a=BigInteger.valueOf(100);
不能使用人们熟悉的算术运算符(如+和*)来处理大数,而需要使用大数类中的add,subtract,divide,mutiply等方法。(注意方法传入的参数也必须是大数,不能是具体的数字)
import java.math.*;
public class HelloJava {
public static void main(String[] args) {
BigInteger a=BigInteger.valueOf(100);
BigInteger b=BigInteger.valueOf(20);
BigDecimal c=BigDecimal.valueOf(100.222);
BigDecimal d=BigDecimal.valueOf(20.222);//静态方法构造大数
BigInteger e=a.add(b);
BigDecimal f=c.multiply(d);
System.out.println(e);
System.out.println(f);
}
}
需要注意的是BigDecimal的divide方法,如果商是一个无限循环小数,那么就需要传入第二个参数RoundingMode,用于对无限循环小数的操作。比如RoundingMode.HALF_UP就是四舍五入。
import java.math.*;
public class HelloJava {
public static void main(String[] args) {
BigDecimal c=BigDecimal.valueOf(100.222);
BigDecimal d=BigDecimal.valueOf(20.222);
BigDecimal f=c.divide(d,RoundingMode.HALF_UP);//divide的第二种写法
System.out.println(f);
}
}
声明数组
Java的声明数组有两种方法——int[] a 和 int a[],大多数程序员喜欢用第一种,因为它可以将类型int[](整型数组)与变量名清晰地分开。
同时Java中要给数组分配空间,使用的语法是
int[] a = new int[100];
注意与C++不同,没有int a[100]的方式。分配空间一定要用new。
当然也可以对数组进行初始化
int[] a = {2,3,5,7,11};
for each循环
这是一种功能很强的循环结构,可以用来一次处理数组(或者其他元素集合)中的每个元素,而不必考虑指定下标值。 这个循环将会遍历数组中的每个元素,而不是下标值。
语法格式:for(variable:collection){} 其中variable表示暂存数组元素的变量名,collection表示一个数组(或实现了Iterable接口的类对象)
public class HelloJava {
public static void main(String[] args) {
int[] a= {1,2,3,4,5,6,7};
for(int t:a) {
System.out.println(t);
}
}
}
在Java种,允许将一个数组变量拷贝到另一个数组变量。这时,两个变量将引用同一个数组——因为Java中的数组名实际上相当于一个数组指针。(int* a = new int[100])
public class HelloJava {
public static void main(String[] args) {
int[] a= {1,2,3,4,5,6,7};
int[] b= a;
b[1]=99;//数组a同样也会发生变化
for(int t:a) {
System.out.println(t);
}
}
}
发现改变数组b的同时,a也发生了变化。
如果需要对二维数组使用for each循环语句,那么需要使用两个嵌套的循环——for each不能自动处理二维数组的每个元素。需要先处理行,再处理行中的元素。
public class HelloJava {
public static void main(String[] args) {
int [][] a = new int[20][20];//声明二维数组的方法
int [][] b = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};//二维数组初始化的方法
for(int[] row:b) {//用row处理每一行
for(int value:row) {//用value处理行中的元素
System.out.print(value+" ");
}
}
}
}
在二维数组中,a[i]表示第i个子数组,也就是第i行,它本身也是一个数组。而a[i][j]则是引用这个数组的第j个元素(准确的说是序号为j的元素)。
在Java中,可以让两行进行交换。
public class HelloJava {
public static void main(String[] args) {
int [][] b = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};//二维数组初始化的方法
int []temp = b[0];
b[0] = b[1];
b[1] = temp;
for(int[] row:b) {
for(int value:row) {
System.out.print(value+" ");
}
}
}
}
对Java二维数组的详细解释
double [][] a = new double[10][6];//声明二维数组并分配空间
在C++中,这句代码并不同于 double a[10][6],也不同于 double (*a)[6] = new a[10][6] 。而是分配了一个包含十个指针的数组。
double** a = new a*[10];
然后,指针数组的每一个元素都被填充了一个包含6个数字的数组。
for(int i=0;i<10;i++)
a[i] = new a[6];