3.1 简单的Java程序
- Java语言中,对大小写敏感。
3.2 注释
Java中的注释不会出现在可执行程序中。在Java中,有三种注释方式:
- 最常用的方式是// ,从//开始到本行结束
- /* */ 将一段比较长的注释括起来。
- /** */ 这种注释用来自动生成文档。
3.3 数据类型
Java是一种强类型语言。 在Java中,一共有8种基本类型(primitive type),其中包含4种整型(byte, short , int , long),2种浮点型(float , double) , 1种字符类型(char) , 1种布尔类型(boolean)。
3.3.1 整型
类型 | 存储需求 | 取值范围 |
---|---|---|
int | 4字节 | -2147483648~2147483647(正好超过20亿) |
short | 2字节 | -32768~32767 |
long | 8字节 | -9223372036854775808~9223372036854775807 |
byte | 1字节 | -128~127 |
注意,Java没有任何无符号类型。
3.3.2 浮点型
类型 | 存储需求 | 取值范围 |
---|---|---|
float | 4字节 | 大约±3.40282347E+38F(有效位6~7位) |
double | 8字节 | 大约±1.79769313486231570E+308(有效位15位) |
double表示类型是float类型的两倍(所以有人又称double为双精度类型),绝大多数时候都用double。float数值后有后缀F(如3.402F),没有F默认为double类型。
3.3.3 char
char类型用来表示单个字符。
3.3.4 布尔值
boolean类型有两个值:false和true,用来逻辑判断。整型值和布尔值之间不能进行相互转换。
3.4 变量
变量名必须是一个以字母开头的由字母、数字和_组成的序列。例如
int name ;
3.4.1 变量初始化
int age = 23 ;
在Java种,变量的声明尽可能地靠近第一次使用的地方,这是一种良好的程序编写风格。
3.4.2 常量
在Java中,利用关键字final声明常量。关键字final表示该变量只能被赋值一次,一旦被赋值,则不能进行修改。习惯上,常量名使用大写。
在Java中,通常希望一个常量能在一个类的多个方法中调用,通常称这些常量为类常量,可以使用staic final
定义一个类常量。
public static final double CM_PER_INCH = 2.54 ;
public static void main(String[] args) {
System.out.println(CM_PER_INCH);
}
const是Java的保留字,但是目前并没有使用。在Java中,必须使用final定义常量
3.5 运算符
整数被0除将会产生一个异常,浮点数被0除将会得到无穷大或NaN结果。
可以在赋值语句中采用一种简化的格式书写二元运算符:
x += 1 ;
等价于
x = x + 1 ;
3.5.1 自增运算符与自减运算符
自增运算可以编写为
int x = 1 ;
x ++ ; //x=2 ;
因为这些运算符改变的是变量的值,所以他的操作数不能是数值,即写作
13 ++ ;
是一条非法语句。
实际上,自增有两种形式,即“前缀型”和“后缀型”。前缀型先自增在进行运算;后缀则相反。例如:
int x = 1 ;
int n = 2* ++x ; //前缀型,x先自增为2,n为4
int n = 2* x++ ; //后缀型,先计算2*x=2,然后x自增为2,n为2
3.5.2 关系运算符与boolean运算符
关系运算符包括:!(非)、==(等于)、<(小于)、<=(小于等于)、>(大于)、>=(大于等于)、&&(逻辑与)、||(逻辑或)。
&&和||按照“短路”的方式求值。如果第一个操作数已经能够确定表达式的值,则第二个操作数就不必计算。
Java中的三元运算符:
condition ? expression1 : expression2 ;
当条件condition为真,计算expression1表达式,否则计算expression2表达式。
3.5.3 位运算符
在处理整型数值时,可以直接对组成整型数值的各位进行操作。这意味着可以使用屏蔽技术获得整数中的各个位。位运算符包括:
&(与) 、|(或) 、 ^(异或) 、~(非)
若用二进制表示的n的从右往左第4位为1,那么
int fourthBitFromRight = ( n & 8 ) / 8 ;
返回1,否则返回0。
此例通过运用2的幂次方的&运算屏蔽了其他位,而只保留其中的某一位。
&与|不是按照短路方式计算的。
和<<运算符将二进制位进行右移或左移。当需要建立位模式屏蔽某些位时,使用这两个运算符十分方便。上面的例子等价于
//1左移3位几位8。右移3位相当于除以8
int fourthBitFromRight = ( n & (1 << 3) ) >> 3 ;
最后, >>>运算符将用0填补高位(即逻辑移位); >>运算符用符号位填充高位(即算术移位)。没有<<<运算符!
3.5.4 数学函数与常量
在Math类中,包含了各种各样的数学函数。
double x = 4 ;
Math.sqrt(x) ; //平方根
Math.pow(x , 2) ; //x的2次方
//三角函数
Math.sin(x) ;
Math.cos(x) ;
Math.tan(x) ;
Math.atan(x) ;
Math.exp(x) ; //指数函数
Math.log(x) ; //对数函数
//表示Π和e的常量
Math.PI;
Math.E ;
3.5.5 数值类型之间的转换
在程序运行时,经常需要将一种数值类型转换为另一种数值类型。
6个实心箭头表示无信息丢失;3个虚心箭头表示可能有精度损失的转换。
当使用两个不同类型的数值进行二元操作时(如n+f,n是int,f是float),先将两个操作数转换为同一类型,然后再进行计算。
- 如果两个操作数有一个是double,另一个操作数转换为double
- 否则,如果其中一个操作数是float,另一个操作数转换为float
- 否则,如果其中一个操作数是long,另一个操作数是转换为long
- 否则,两个操作数都转换为int
3.5.6 强制类型转换
在必要的时候,int会自动转换为double。但另一方面,也有double需要转换为int的时候。在Java中,允许进行这种数值间的类型转换。当然,有可能会丢失一些信息。在这种情况下,需要通过强制类型转换(cast)实现这个操作。
double x = 9.997
int nx = (int) x ;
这样,nx变量的值为9。强制类型转换通过截断小数部分将浮点值转换为整型
如果想对浮点数进行舍入运算,以便得到最接近的整数(在很多情况下,希望使用这种操作方式),那么就需要使用Math.round()方法。
double x = 9.997 ;
int nx = (int)Math.round(x) ;
现在nx的值为10.当调用round的时候,仍然需要使用强制类型转换(int)。其原因是round方法返回的结果为long类型。
不要在boolean类型与任何数值类型之间进行强制类型转换
3.5.7 括号与运算符级别
下图给出运算符级别。同一个级别的运算符按照从左到右的次序计算(除了表中给出的右结合运算符外)。
例如
因为+=是右结合运算符,所以表达式
a += b += c ;
等价于
a += (b += c)
3.5.8 枚举类型
从JDK5.0开始,可以自定义枚举类型。枚举类型包括有限个命名的值。
例如
enum Size {
SMALL ,
MEDIUM ,
LARGE ,
EXTRA_LARGE
};
现在,可以生命这样一种类型变量:
Size s = Size.MEDIUM ;
Size类型的变量只能存储这个类型声明中给定的某个枚举值,或者null值,null表示这个变量没有设置任何值。
3.6 字符串
Java没有内置的字符串类型,而是在标准的Java类库中提供了一个预定义类String。每个用双引号括起来的字符串都是String类型的一个实例。
String e = "" ; //空字符串
String greeting = "Hello" ;
3.6.1 子串
String类型的**substring()**可以从一个较大的字符串提取一个子串。
String greeting = "Hello" ;
String s = greeting.substring(0,3) ; //区间左闭右开
//s= "Hel"
substring()的工作方式有一个优点:容易计算子串的长度。字符串s.substring(a,b)的长度为b-a。
3.6.2 拼接
与绝大多数程序设计语言一样,Java语言允许使用+拼接两个字符串。
String greeting = "Hello" ;
//String s = greeting.substring(0,3) ;
String s = "World!" ;
System.out.println(greeting+s);
//输出 HelloWorld!
当将一个字符串与一个非字符串的值进行拼接时,后者被转换成字符串
3.6.3 不可变字符串
String类没有提供用于修改字符串的方法。由于不能修改字符串中的字符,所以在Java文档中将String类对象称为不可变字符串。
不可变字符串有一个优点:编译器可以让字符串共享。
为了弄清具体的工作方式,可以想象将各种字符串存放在公共的存储池中。字符串变量指向存储池中的对应位置。如果复制一个字符串变量,原始字符串与复制的字符串指向存储池中的相同位置,共享相同的字符。
3.6.4 检测字符串是否相等
可以使用equals方法检测两个字符串是否相等。
s.equals(t) ;
想要检测两个字符串是否相等,但不区分大小写,可以使用equalsIgnoreCase()。
System.out.println("HELLO".equalsIgnoreCase("hello"));
//返回TRUE
一定不能使用运算符检测两个字符串是否相等! 这个运算符只能够确定两个字符串是否放置在同一个位置上。
如果虚拟机始终将相同的字符串共享,就可以使用检测是否相等。但实际上只有字符串常量是共享的,而+或substring等操作产生的结果并不是共享的。
== 与 equals的却别
- 对于==,如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等;
如果作用于引用类型的变量,则比较的是所指向的对象的地址
- 对于equals方法,注意:equals方法不能作用于基本数据类型的变量
如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;
诸如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容。
3.6.6 字符串API
汇总常用的一部分String方法
//按照字典顺序,如果字符串位于other之前,返回负数,在other之后,返回正数,相等返回0
int compareTo(String other)
//如果字符串以suffix结尾,返回true
boolean endsWith(String suffix)
//如果字符串与other相等,返回true
boolean equals(Object other)
//如果字符串相等(忽略大小写),返回true
boolean equalsIgnoreCase(String other)
//返回与字符串str匹配的第一个子串的开始位置,这个位置从0或者fromIndex开始计算。如果找不到,返回-1
int indexOf(String str)
int indexOf(String str , int fromIndex)
//返回与字符串str匹配的最后一个子串的开始位置,这个位置从原始串的尾端或fromIndex开始计算。
int lastIndexOf(String str)
int lastIndexOf(String str , int fromIndex)
//返回字符串的长度
int length()
//返回一个新字符串。这个字符串用newString代替原始字符串中所有oldString
String replace(String oldString , String newString)
//如果字符串以preffix开始,返回true
boolean startsWith(String preffix)
//返回一个新字符串。这个字符串包含原始字符串[beginIndex,endIndex)的所有字符
String substring( int beginIndex )
String substring( int beginIndex , int endIndex )
//返回一个新字符串。这个字符串将原始字符串中所有大写字母改成小写
String toLowerCase()
//返回一个新字符串。这个字符串将原始字符串中所有小写字母改成大写
String toUpperCase()
//返回一个新字符串。这个字符串删除了原始字符串头部和尾部的空格
String trim()
3.6.8 构建字符串
有些时候,需要由较短的字符串构建字符串。若每次都拼接字符串,都会构建一个新的String对象,既耗时又浪费空间。使用StringBuilder类可以避免这个问题。
StringBuilder builder = new StringBuilder();
//当需要添加一部分内容时,调用append()方法
builder.append("Hello");
builder.append("World");
//使用toString()方法,可以得到一个String对象
String str = builder.toString() ;
System.out.println(str);
//输出 HelloWorld
在JDK5.0中引入StringBuilder类。这个类的前身是StringBuffer,其效率略微有些低,但允许使用多线程的方式执行添加或删除字符的操作。如果所有字符串在一个单线程中(通常都是这样)编辑,则应该使用StringBuilder替代它。这两个类的API是相同的。
下面是StringBuilder类中的重要方法
//构造一个空的字符串构建器
StringBuilder()
//返回构建器或缓冲器中的字符数量
int length()
//追加一个字符串兵返回this
StringBuilder append(String str)
//将第i个字符改为为c
void setCharAt(int i , Char c)
//在offset位置插入一个字符串并返回this
StringBuilder insert(int offset , String str)
//删除[startIndex , endIndex)的字符
StringBuilder delete( int startIndex , int endIndex )
//返回一个与构建器或缓冲器内容相同的字符串
String toString()
3.7 输入与输出
3.7.1 读取输入
要想通过控制台进行输入,首先需要构造一个Scanner对象,并于“标准输入流”System.in关联
Scanner in = new Scanner(System.in) ;
//输入一行字符串(以回车结束)
String line = in.nextLine() ;
//输入一个单词(以空格结束)
String name = in.next() ;
//输入一个整数
int age = in.nextInt() ;
//输入一个浮点数
double price = in.nextDouble() ;
3.8 控制流程
3.8.1 条件语句
if( ){
}
else if( ){
}
else{
}
3.8.2 循环
while( ){
}
或者
do{
}while( ) ;
3.8.4 确定循环
for循环语句是支持迭代的一种通用结构
for( int i = 0 ; i < 10 ; i++ ){
//TODO
}
3.8.6 终端控制流程语句
Java提供了一种带标签的break语句,用于跳出多种嵌套的循环语句。标签必须放在希望跳出的最外层循环之前,并且紧跟一个冒号。
read_data:
while( true ){
for( int i = 0 ; i < 10 ; i++ ){
break read_data;
}
}
注意:break只能跳出语句块,而不能跳入语句块。
continue语句将控制转移到最内层循环的首部。
3.9 大数值
如果基本的整数和浮点数精度不能够满足需求,那么可以使用java.math包中的两个很有用的类:BigInteger和BigDecimal。这两个类可以处理包含任意长度数字序列的值。BigInteger类实现了任意精度的整数运算,BigDecimal实现了任意精度的浮点数运算。
使用静态的valueOf方法可以将普通的数值转换为大数值:
BigInteger a = BigInteger.valueOf(100) ;
遗憾的是,大数值类型不能使用人们熟悉的算术运算符(+和*等)。而需要使用大数值类的add和multiply方法
BigInteger a = BigInteger.valueOf(100) ;
BigInteger c = BigInteger.valueOf(100) ;
BigInteger b = a.add(c) ; // b = a+c
BigInteger常用API
//返回这个大整数与other的和、差、积、商以及余数
BigInteger add(BigInteger ohter)
BigInteger subtract(BigInteger other)
BigInteger multiply(BigInteger other)
BigInteger divide(BigInteger other)
BigInteger mod( BigInteger other )
//大整数比ohter大,返回正数,比other小返回负数,相等返回0
int compareTo(BigInteger other)
//返回值等于x的大整数
static BigInteger valueOf(long x)
3.10 数组
数组是一种数据结构,用来存储同一类型值的集合。
在声明数组变量时,需要指出数组类型(数据元素类型紧跟[])和数组变量名字。
int[] a ;
这条语句只是声明了变量a,并没有将a实例化。
int[] a = new int[100] ;
一旦创建了数组,就不能再改变它的大小。
3.10.1 For each循环
Java SE 5.0增加了一种功能很强的循环结构,可以依次处理数组中的每个元素而不必为指定下标值而分心。
格式为
for( variable : collection ){
//TODO
}
定义一个变量variable暂存集合中的每一个元素,并执行相应语句。collection这一集合表达式必须是一个数组或者是一个实现了Iterator接口的类对象。
例如
int[] a = { 1 ,2,3 } ;
for (int element : a) {
System.out.println(element);
}
3.10.2 数组初始化以及匿名数组
在Java中,提供一种创建数组对象并同时赋予初始值的简化书写方式。
int[] a = { 1 ,2,3 } ;
请注意,在这种语句中不需要调用new
匿名数组
new int[]{1,2,3} ;
3.10.3 数组拷贝
在Java中,允许将一个数组变量拷贝给另一个数组变量。这时, 两个变量将引用同一个数组
int[] a = {1,2,3,4,5} ;
int[] b = a ;
b[0] = 6 ;
for (int element : a) {
System.out.println(element);
}
//输出结果为 6,2,3,4,5 即修改b,a也会改变
如果希望将一个数组的所有制拷贝到一个新的数组中去,就要使用Arrays类的copyOf方法:
int[] b = Arrays.copyOf(a , a.length) ;
//第2个参数是新数组b的长度,这个方法通常同来增加数组的大小
3.10.5 数组排序
要想对数组排序,可以使用Arrays类中的sort方法
int[] a = new int[100]
...
Arrays.sort(a) ;
这个方法使用了优化的快速排序。
数组常用API
//返回a中数据元素的字符串,这些数据元素被放在一个中括号内,并用逗号分隔
static String toString( type[] a )
//返回与a类型相同的数组,其长度为length或者end-start,数组元素为a的值
static type copyOf( type[] a , int length )
static type copyOf( type[] a , int start , int end )
//采用优化的快速排序对数组进行排序
static void sort( type[] a )
//采用二分搜索算法查找v。如果成功返回其下标,否则返回一个负数r。-r-1是为了保持a有序v应插入的位置
static int binarySearch( type[] a , type v )
static int binarySearch( type[] a , int start , int end , type v )
//将数组的所有数据元素值设置为v
static void fill( type[] a , type v )
//如果两个数组大小相同,并且下标相同的元素都对应相等,则返回true
static boolean equals( type[] a , type b )
3.10.6 多维数组
在Java中声明一个二维数组相当简单
double[][] balances ;
要想快速打印一个二维数组,可以调用
Arrays.deepToString(a);
3.10.7 不规则数组
不规则数组即每一行有不同的长度。要想创建一个不规则的数组,首先需要分配一个具有所含行数的数组
int[][] odds = new int[3][] ;
接下来分配这些行
for( int n = 0 ; n < 3 ; n++ ){
odds[n] = new int[n+1] ;
}