核心技术:基本程序设计结构
数据类型
类型 | 存储需求 | 默认值 | 取值范围 |
---|---|---|---|
整型 | |||
int | 4字节 | 0 | -2 147 483 648 - 2 147 483 647
|
short | 2字节 | 0 | -32 768 - 32767
|
long | 8字节 | 0L | -9 223 372 036 854 775 808 - 9 223 372 036 854 775 807
|
byte | 1字节 | 0 | -128 - 127
|
浮点 | |||
float | 4字节 | 0.0f | 大约 +- 3.402 823 47E+38F(有效位数为6、7位)
|
double | 8字节 | 0.0d | 大约 +- 1.797 693 134 862 315 70E+308(有效位数为15位)
|
字符 | |||
char | 2字节 | 'u0000' | \u0000 - \uffff |
布尔 | |||
boolean | 1位 | false | true | false |
- 常量“Double.POSITIVE_INFINITY”、“Double.NEGATIVE_INFINITY”、“Double.NAN”分别表示“正无穷大”、“负无穷大”、“NaN(不是一个数字)”;
取值范围
- 整数用原码,负数用补码表示
以int为例,在java中占4字节,即32位:
- 其中第一位为符号位(0正,1负)
- 所以负数范围为:-2^31 = -2147483648
- 整数的范围为:2^31-1 = 2147483647 (减去全为0时的情况,0无正负)
即:-2147483648 - 2147483647 (-2^31 — 2^31-1)
关于0.2 + 0.1 不等于 0.3
使用自然类型计算小数的时候,会出现:“0.2+0.1=0.30000000000000004”,而“0.1+0.6=0.7”的情况:
其原因如下:
计算机存储、计算或者展示,都需要转换2进制。 在现实世界中,数字主要有整数和小数两种,整数包括正整数、负整数以及零。 计算机中表示整数的方式有很多,如原码、反码以及补码等。 在计算机中存储的整数则分为有符号数和无符号数。 对于无符号数,采用哪种编码方式都无所谓,对于有符号数的编码方式,常用的是补码。 那么,一个十进制数字想要获得其二进制的补码,需要先通过一定的算法得到他对应的原码。
- 十进制整数转换为二进制,可以采用“除2取余,逆序排列”,或者xxx法(列出“...128,64,32,26,8,4,2,1”,根据十进制数在对应位置1或0);
- 十进制小数转换为二进制:则采用“乘2取整,顺序排列”的方法;
如,0.625的二进制转换如下:
而0.1的二进制:
可以得知:0.1的二进制转换中出现了无限循环的情况,也就是(0.1)10 = (0.000110011001100…)2,而不能把0.1转换为确定的二进制数
即计算机无法用二进制精确的表示0.1
IEEE 754规定了四种表示浮点数值的方式:单精确度(32位)、双精确度(64位)、延伸单精确度(43比特以上,很少使用)与延伸双精确度(79比特以上,通常以80位实现)
但仅是用近似值表示小数,并非真实值,如果使用float、double等类型进行小数计算,仍然会丢失数值。并没有解决问题。
所以:为了解决这样的精度问题,Java中提供了BigDecimal来进行精确运算。
- BigDecimal 并不是Java的数据类型,而是一个Java对象!
变量
- 变量名:以字母开头,并由字母和数字构成的序列;(字母包括:'A'-'Z'、'a'-'z'、'_'、'$'、'ä')
- 不能使用java保留字作为变量名;
- 变量名对大小写敏感;
常量
- java 中利用关键字“final”指示常量;(表示变量只能被赋值一次)
- 习惯上,常量名使用全大写;
- 如果需要某个常量在一个类的多个方法中使用,则将其设置为类常量,用“static final”修饰;
运算符
常用运算:
- “+”:加
- “-”:减
- “*”:乘
- “/”:除
- “%”:取余(取模)
- 使用“stricfp”关键字标记的方法、类,其中的所有指令都要使用严格的浮点计算;【?没用过】
数学函数与常量
- 使用java.lang.Math
- 如果要得到一个完全可预测的结果,应该使用“StrictMath”【?】
Math 类中提供了各种数学函数:
- 平方根:“double y = Math.sqrt(x);”
- 幂运算:“double y = Math.pow(x, a);”
- 三角函数:
- Math.sin
- Math.cos
- Math.tan
- Math.atan
- Math.atan2
- 指数函数、自然对数、以10为底的对数:
- Math.exp
- Math.log
- Math.log10
- 常量π、e的近似值:(近似值!)
- Math.PI
- Math.E
数值类型间的强制转换
- 如果其中一个为double,则另一个会被转换为double;
- 否则,如果如果其中一个为float,则另一个会被转换为float;
- 否则,如果如果其中一个为long,则另一个会被转换为long;
- 否则,两操作数都会被转换为int;
强制类型转换
强制类型转换(cast):用于类似由double向int、float这类,可能会丢失信息的转换。
如:
double x = 9.997;
int nx = (int)x;
如果需要对浮点数进行舍入运算,以便得到最接近的整数,则用“Math.round”(其返回值为long):
double x = 9.997;
int nx = (int)Math.round(x);
关系运算
- “<”:小于
- “>”:大于
- “<=”:小于等于
- “>=”:大于等于
- “&&”:逻辑与
- “||”:逻辑或
- “!”:逻辑非
- “?:”:三目运算
- “&&”、“||”是按照“短路”方式来求值的(即:如果第一个操作数能确定表达式的值,则不再计算第二个表达式)
- 如:“expression1 && expression2”:如果“expression1”为false,则不再计算“expression2”
- 如:“expression1 || expression2”:如果“expression1”为true,则不再计算“expression2”
位运算
- “&”:and
- “|”:or
- “^”:xor(亦或,相同为真,相异为假)
- “~”:not
- “>>”:右移(用符号位填充高位)
- “<<”:左移
- “>>>”:无符号右移(用0填充高位)
- 这些运算符,按照位模式处理;(把操作数的二进制数,按照从右至左,对每一位进行运算)
- 移位运算符的右操作数要完成模32的运算(若左操作数为long,则右操作数模64)【如果移动的位数超过了该类型的最大位数,那么编译器会对移动的位数取模】
- 如:“1<<35”等同于“1<<3”或8()
- 【猜测:对于4字节(32位)的类型来说,位移超过了32位,就会从另一侧出来(就像转动一个环?)】
位移示例
7右移2位:
00000000 00000000 00000000 00000111//7的二进制
右移两位后
00000000 00000000 00000000 00000001//最左边用符号位填充
转换十进制为:
1
7无符号右移2位:
00000000 00000000 00000000 00000111//7的二进制
右移两位后
00000000 00000000 00000000 00000001//最左边用0填充
转换十进制为:
1
-7右移2位:
11111111111111111111111111111001//-7的二进制
右移两位后
11111111111111111111111111111110//最左边用符号位填充
转换十进制为:
-2
-7无符号右移2位:
11111111111111111111111111111001//-7的二进制
右移两位后
00111111111111111111111111111110//最左边用0填充
转换十进制为:
1073741822
7左移2位:
00000000 00000000 00000000 00000111//7的二进制
右移两位后
00000000 00000000 00000000 00011100//最右边用0填充
转换十进制为:
28
-7左移2位:
11111111111111111111111111111001//-7的二进制
右移两位后
11111111111111111111111111100100//最右边用0填充
转换十进制为:
-28
- 位移只能用于整型
- >>> 不能用于负数的右移(如上,高位补0,移成整数了)
- >> 不能用于大于整型范围的操作数(运算符除“/”也不行,只能用“>>>”):
- 操作数大于整型范围时,最高位(符号位)可能为1(即变成了负数),用“>>”导致高位用 1 补足,成为负数;
枚举类型
枚举类型:用于取值在一个有限的集合范围内。
定义:
enum Size{SMALL, MEDIUM, LARGE, EXTRA_LARGE};
声明:
Size s = Size.MEDIUM;
字符串
Java没有内置的字符串类型,而是在标准Java类库中提供了一个预定义类“String”,每个双引号中的字符串都是String类的一个实例。
从概念上讲,Java字符串就是Unicode字符序列。
子串
从较大的字符串中提取出一个字串:
String greeting = "Hello";
String s = greeting.subString(1,3); // s="ell";
- 从1开始到3为止(不包括第三位,即第1、2位),长度为3-1=2。
拼接
- Java允许使用“+”连接(拼接)两个字符串。
把多个字符串放在一起,用一个定界符分隔:
String all = String.join(" / ", "S", "M", "L", "XL"); // all="S / M / L / XL"
不可变字符串
Java没有提供修改字符串的方法,如果需要修改字符串中的字符,需要截取字串再进行拼接。
所以,Java文档中将String对象称为“不可变字符串”。
以“greeting="Hello"”为例,“Hello”存在于常量池中,其内容不会改变。只能修改greeting将其指向新的字符串。
String、StringBuffer 和 StringBuilder
- String:位于常量池中(不可修改);
- StringBuffer:位于缓冲区(可修改),慢,安全;
- StringBuilder:位于Java堆上(可以修改),快,不安全;
检测字符串是否相等
- 判断字符串是否相等,应该用“s.equals(t)”(比较值),而非“==”(比较引用地址)
- 如需要不区分大小写进行比较,则用“equalsIgnoreCase”。
空串 与 Null串
空串:
- 空串是一个java对象,有自己的长度(0)和内容(空)
- 可以用“if(str.length()==0)”或“if(str.equals(""))”判断是否空串
Null:
- null表示当前没有任何对象与该变量关联
- 可以用“if(str == null)”判断是否为空
有时候需要判断既不是null,也非空串:
“if(str != null && str.length() != 0)”
- 必须先判断null(逻辑运算符的短路方式),否则如果对null调用length会出错
码点与代码单元
代码点(Code Point):在 Unicode 代码空间中的一个值,取值 0x0 至 0x10FFFF,代表一个字符。
代码单元(Code Unit):在具体编码形式中的最小单位。比如 UTF-16 中一个 code unit 为 16 bits,UTF-8 中一个 code unit 为 8 bits。
- 一个 code point 可能由一个或多个 code unit(s) 表示
- 在Java中,char类型描述了UTF-16编码中的一个代码单元
java字符串由char值序列组成,char类型是一个采用UTF-16编码表示Unicode码点的代码单元。大多数的常用Unicode字符使用一个代码单元就可以表示,而辅助字符需要一对代码单元表示。
- “greeting.length()”:返回的是代码单元的数量
- “greeting.codePointCount(0, greeting.length())”:实际长度(码点数量)
- “greeting.charAt(3)”:返回位置3的代码单元
- “int index=greeting.offsetByCodePoint(0,i)”“int cp=greeting.codePointAt()”:得到第i个码点
- “int[] codePoints = str.codePoints().toArray();”:将String转化为码点数组,用于遍历
- “String str = new String(codePoints, 0, codePoints.length());”:将码点数组转化为String
String API
常用:
- “int compare(String other)”:按照字典顺序,比较字符串(负数:位于other之前;正数:位于other之后;0:两个字符串相等)
- “boolean equals(Object other)”:
- “boolean euqalsIgnoreCase(String other)”:
- “boolean startWith(String prefix)”:如果string以prefix开头,返回true
- “boolean endWith(String sufffix)”:
- “int indexOf(...)”:
- “int lastIndexOf(...)”:
- “int length()”:
- “String replace(CharSequence oldString, CharSequence newString)”:返回一个新串。用newString代替原始串中所有oldString。可以用String或StringBuilder作为CharSequence参数。
- “String suString(...)”:
- “String toLowerCase()”:
- “String toUpperCase()”:
- “String trim()”:
- “String join(...)”:
构建字符串
在使用多个短字符串拼接构建新字符串的过程中,每一次拼接都会生成一个String对象,耗时且浪费空间。
StringBuilder 类旧用于避免此问题:
StringBuilder builder = new StringBuilder();
//拼接字符串ch、str形成新串
builder.append(ch);
builder.append(str);
//构建字符串时,使用toString
String completedString = builder.toString();
- 在 JDK 5.0 中引入了 StringBuilder 类,其前身是 StringBuffer(其效率稍低,但允许多线程执行),如果所有字符串在一个单线程中编辑,则应该使用 StringBuilder。
输入输出
标准输入输出
标准输出流:(控制台窗口)
System.out.pringtln();
标准输入流:
// 构造 Scanner 对象,并于“标准输入流”(System.in)关联
Scanner in = new Scanner(System.in);
// 读取输入行
String name = in.nextLine();
// 读取字符串(以空格符作为分隔)
String firstName = in.next();
// 读取整数
int age = in.nextInt();
//读取浮点数
double salary = in.nextDouble();
- Scanner 位于 java.util 包中。(当使用的类定义不在 java.lang 包时,需要 import 相关包)
Scanner 不适用于从控制台读取密码(输入可见),所以 Java SE 6 引入了 “Console”类:
Console cons = System.console();
String username = cons.readLine("User name: ");
char[] passwd = cons.readPassword("Password: ");
- Console 每次只能读取一行,不能读取一个单词或数值;
- 密码应该存在一维字符数组中,而非字符串中。
- 对密码处理完成之后,应该马上用一个填充值覆盖数组元素。
格式化输入输出
Java SE 5.0 沿用了 C 语言库函数中的 “printf”方法:
System.out.printf("%8.2f", x);
转换符 | 类型 | 示例 | 转换符 | 类型 | 示例 | |
---|---|---|---|---|---|---|
d | 十进制整数 | 158 | | | s | 字符串 | Hello |
x | 十六进制整数 | 9f | | | c | 字符 | H |
o | 八进制整数 | 237 | | | b | 布尔 | True |
f | 定点浮点数 | 15.9 | | | h | 散列码 | 42628b2 |
e | 指数浮点数 | 1.59e+01 | | | tx 或 Tx | 日期时间(T强制大写) | (已过时,改用“java.time”类 |
g | 通用浮点数 | — | | | % | 百分号 | % |
a | 十六进制浮点数 | 0x1.fccdp3 | | | n | 与平台相关的分隔符 | — |
文件输入输出
要对文件进行读取,就要用“File”对象构造一个“Scanner”对象:
Scanner in = new Scanner(Path.get("myfile.txt"), "UTF-8");
- 需要对反斜杠“\”进行转义“\\”
然后就可以利用 Scanner 对象的方法对文件进行读取
要写入文件,就需要构造一个“PrintWriter”对象(构造其中只需要文件名):
PrintWriter out = new PrintWriter("myfile.txt", "UTF-8");
- 如果该文件不存在,则创建
然后可以使用 PrintWriter 的 print、printf、println 等方法对文件进行写入
流程控制
java 的流程控制结构与 c、c++ 基本一样。
块
块(block),由大括号括起来的若干条简单的Java语句。
- 块确定了变量的作用域:同一个块、嵌套的块中,不能声明同名的变量
条件
if else
循环
- while
- do while
- for
- 变形的 for 循环,类似于 C# 中的foreach:
for( variable : collection )
多重选择
switch
case 标签可以是:
- 类型为 char、byte、short、int 的常量表达式
- 枚举常量
- 从 Java SE 7 开始,还可以是字符串字面量(如:"yes")
中断控制
Java提供了一种“带标签的break”,用于跳出多重嵌套的循环:
(标签放在希望跳出的最外层循环之前,其后带“:”)
...
read_date:
while(){
...
for(...){
...
if(){
...
break read_date;
}
}
...
}
...
- 标签可以用到任何语句块中,不局限于循环
continue,用于中断正常的控制流程(越过当前循环体的剩余部分),而转移到当前循环的首部。
- 带标签的 continue,会天跳到与标签匹配的循环首部
大数值
- 位于 java.math 包中
包括了“BigInteger”(整数运算)和“BigDecimal”(浮点运算),可以处理任意长度的数字序列的数值。
- 使用静态的“valueOf”方法,可以将普通数值转换为大数值:
BigInteger a = BigInteger.valueOf(100);
- 不能使用基本的算数运算来处理大数据,而应使用大数值类中的add、multiply等方法:
BigInteger c = a.add(b); // c = a + b BigInteger d = c.multiply(b.add(BigInteger.valueOf(2))); // d = c * (b + 2)
BigInteger 方法:(大整数)
- “BigInteger add(BigInteger other)”:与另一个BigInteger的和;
- “BigInteger subtract(BigInteger other)”:...差;
- “BigInteger multiply(BigInteger other)”:...积;
- “BigInteger divide(BigInteger other)”:...商;
- “BigInteger mod(BigInteger other)”:...余数;
- “int compareTo(BigInteger other)”:与另一个BigInteger比较(0:相等;正数:这个大;负数:另一个大);
- “static BigInteger valueOf(long x)”:返回值等于x的大整数;
BigDecimal 方法:(大实数)
- “BigDecimal add(BigDecimal other)”:...;
- “BigDecimal subtract(BigDecimal other)”:...;
- “BigDecimal multiply(BigDecimal other)”:...;
- “BigDecimal divide(BigDecimal other, RoundingMode mode)”:...;
- 计算商必须给出摄入方式,如“RoundingMode.HALF_UP”(四舍五入)
- “BigDecimal mod(BigDecimal other)”:...;
- “int compareTo(BigDecimal other)”:...;
- “static BigDecimal valueOf(long x)”:...;
- “static BigDecimal valueOf(long x)”:返回值为x/10^scale的大实数;
数组
- “int[] a;”:声明数组;
- 与“int a[]”同
- “a = new int[100];”:初始化数组;
- “new int[]”用于创建数组
初始化时:
- 若为数字数组,所有元素初始化为0;
- boolean,false
- object,null
- 数组创建之后大小不再改变
- 若在使用中需要扩展大小,应用数据列表(array list)
for each 循环
for(variable : collection) statement
- collection 必须是一个数组,或实现了“Iterable”接口的对象(如:ArrayList)
- Iterable:迭代接口;Iterator:对象内容的迭代器;
- 在不需要遍历元素,或需要使用元素下标的情况下,仍然推荐使用传统for循环
如:
for (int element : a)
{
System.out.println(element);
}
数组初始化 及 匿名数组
创建同时赋予初始值
int a = {1,2,3,4,5,6,7,8,9};
匿名数组:
new int[] {1,2,3,4,5,6,7,8,9}
- 可以在不创建新变量的情况下初始化一个数组
数组拷贝
将一个数组变量拷贝给另一个数组变量,则,两个数组变量将引用同一个数组:
int b = Arrays.copyOf(a, a.length);
如果需要在拷贝时增加新数组的大小:
int b = Array.copyOf(a, a.length * 2); //大小扩大一倍
数组排序
对数值型数组进行排序:
int a = new int[10000];
...
Arrays.sort(a); // 优化的快速排序
Arrays常用方法
- “static String toString(type[] a)”:;
- “static type copyOf(type[] a, int length)”:;
- “static type copyOfRange(type[] a, int start, int end)”:;
- “static void sort(type[] a)”:;
- “static int binarySearch(type[] a, type v)”:;
- “static int binarySearch(type[] a, int start, int end, type v)”:;
- “static void fill(type[] a, type v)”:;
- “static boolean equals(type[] a, type[] b)”:如果两个数组大小相同,且下标对应的元素相等,则返回true;
多维数组
快速打印一个二维数组的元素列表:
System.out.println(Arrays.deepToString(a)); //输出格式为:[[1,2,3],[4,5,6],[7,8,9]]