- 一.数据类型
- 1.1 整型
- 1.2 浮点类型
- 1.3 char类型
- Unicode 和 char 类型
- 1.4 boolean 类型
- 二.变量
- 三.运算符
- 3.1 数值类型之间的转换
- 3.2 强制类型转换
- 3.3 结合赋值和运算符
- 3.4 自增与自减运算符
- 3.5 关系和 boolean 运算符
- 3.6 位运算符
- 3.7 括号与运算符级别
- 四.字符串
- 4.1 拼接
- 4.2 不可变字符串
- 4.3 检测字符串是否相等
- 4.4 空串与NULL串
- 4.5 码元与代码单元
- 4.6 构建字符串
- 4.7 格式化输出
- 五.字符串常量池常见问题(非书内内容,参考JavaGuide)
- 5.1 String 类型的变量和常量做"+"运算时发生了什么?
- 5.2 String s1 = new String("abc");这句话创建了几个字符串对象?
- 六.控制流程
- 6.1 块作用域
- 6.2 循环
- 七.大数值
- 八.数组
- 8.1 for each 循环
- 8.2 数组初始化以及匿名数组
- 8.3 数组拷贝
- 8.4 命令行参数
- 8.5 数组排序
- 8.6 不规则数组
Java 是一种强类型语言。这就意味着必须为每一个变量声明一种类型:在 Java 中,一共有 8种基本类型(primitive type),其中有 4 种整型、2 种浮点类型、1 种用于表示 Unicode 编码的字符单元的字符类型 char 和 1 种用于表示真值的 boolean 类型。
1.1 整型1.整型用于表示没有小数部分的数值,它允许是负数。Java 提供了 4 种整型:
2.长整型数值有一个后缀 L 或 l ( 如 4000000000L) 。
3.注意,Java 没有任何无符号(unsigned) 形式的 int、long、short 或 byte 类型。
1.2 浮点类型1.浮点类型用于表示有小数部分的数值。double表示这种类型的数值精度是float类型的两倍(有人称之为双精度数值)。
2.float 类型的数值有一个后缀 F 或 f (例如,3.14F) 。没有后缀 F 的浮点数值(如 3.14 ) 默认为 double 类型。当然,也可以在浮点数值后面添加后缀 D 或 d (例如,3.14D)。
3.下面是用于表示溢出和出错情况的三个特殊的浮点数值(一个正整数除以 0 的结果为正无穷大。计算 0/0 或者负数的平方根结果为 NaN)
- 正无穷大,如Double_POSITIVE_INFINITY
- 负无穷大,如Double.NEGATIVEJNFINITY
- NaN (不是一个数字),如Double.NaN
4.不能这样检测一个特定值是否等于 Double.NaN。所有"非数值"的值都认为是不相同的。然而,可以使用 Double.isNaN 方法:
//错误 if (x = Double.NaN)// is never true //正确 if (Double.isNaN(x)) // check whether x is "not a number"
5.浮点数值不适用于无法接受舍入误差的金融计算中。 例如,命令 System.out.println( 2.0-1.1 ) 将打印出0.8999999999999999,而不是人们想象的 0.9。这种舍入误差的主要原因是浮点数值采用二进制系统表示,而在二进制系统中无法精确地表示分数 1/10。这就好像十进制无法精确地表示分数 1/3—样。如果在数值计算中不允许有任何舍入误差,就应该使用 BigDecimal类。
1.3 char类型1.char 类型原本用于表示单个字符。不过,现在情况已经有所变化。 如今,有些 Unicode字符可以用一个 char 值描述,另外一些 Unicode 字符则需要两个 char 值。
2.char 类型的字面量值要用单引号括起来。例如:‘A’ 是编码值为 65 所对应的字符常量。
3.char 类型的值可以表示为十六进制值,其范围从 u0000 到 Uffff。
4.除了转义序列 u 之外, 还有一些用于表示特殊字符的转义序列。所有这些转义序列都可以出现在加引号的字符字面量或字符串中。例如,’u2122’ 或 “Hellon”。
//直接指定单个字符作为字符值 char aChar = 'a'; //使用转义字符来作为字符值 char enterChar = 'r'; //使用Unicode编码值来指定字符值 char ch = 'u2122'; //将输出一个'™'字符 System.out.println(ch); //定义一个'疯'字符值 char zhong = '疯'; //直接将一个char变量当成int类型变量使用 int zhongValue = zhong; System.out.println(zhongValue); //直接把一个0~65535范围内的int整数赋给一个char变量 char c = 97; System.out.println(c);
5.注意:Unicode 转义序列会在解析代码之前得到处理。 例如,"u0022+u0022"并不是一个由引号(U+0022) 包围加号构成的字符串。 实际上,u0022会在解析之前转换为", 这会得到""+"",也就是一个空串。
Unicode 和 char 类型1.要想弄清 char 类型,就必须了解 Unicode 编码机制。Unicode 打破了传统字符编码机制的限制。 在 Unicode 出现之前,已经有许多种不同的标准:美国的 ASCII、西欧语言中的ISO 8859-1、俄罗斯的 KOI-8、中国的 GB 18030 和 BIG-5 等。这样就产生了下面两个问题:一个是对于任意给定的代码值,在不同的编码方案下有可能对应不同的字母;二是采用大字符集的语言其编码长度有可能不同。例如,有些常用的字符采用单字节编码,而另一些字符则需要两个或更多个字节。
2.设计 Unicode 编码的目的就是要解决这些问题。在 20 世纪 80 年代开始启动设计工作时,人们认为两个字节的代码宽度足以对世界上各种语言的所有字符进行编码,并有足够的空间留给未来的扩展。十分遗憾, 经过一段时间,不可避免的事情发生了。Unicode 字符超过了 ==65536 (2的16次方)==个,其主要原因是增加了大量的汉语、日语和韩语中的表意文字。现在,16 位的 char 类型已经不能满足描述所有 Unicode 字符的需要了。
3.下面利用一些专用术语解释一下 Java 语言解决这个问题的基本方法。从 Java SE 5.0 开始。码点(code point) 是指与一个编码表中的某个字符对应的代码值。在 Unicode 标准中,码点采用十六进制书写,并加上前缀 U+,例如 U+0041 就是拉丁字母 A 的码点。Unicode 的码点可以分成 17 个代码级别(code plane)。第一个代码级别称为基本的多语言级别(basicmultilingual plane),码点从 U+0000 到 U+FFFF,其中包括经典的 Unicode 代码;其余的 16个级别码点从 U+10000 到 U+10FFFF,其中包括一些辅助字符(supplementary character)。
4.UTF-16 编码采用不同长度的编码表示所有 Unicode 码点。在基本的多语言级别中,每个字符用 16 位表示,通常被称为代码单元( code unit);而辅助字符采用一对连续的代码单元进行编码。这样构成的编码值落入基本的多语言级别中空闲的 2048 字节内,通常被称为替代区域(surrogate area) [ U+D800 ~ U+DBFF 用于第一个代码单元,U+DC00 ~ U+DFFF 用于第二个代码单元 ]。这样设计十分巧妙,我们可以从中迅速地知道一个代码单元是一个字符的编码,还是一个辅助字符的第一或第二部分。例如,핆是八元数集的一个数学符号,码点为 U+1D546, 编码为两个代码单元 U+D835 和U+DD46。
5.在 Java 中,char 类型描述了 UTF-16 编码中的一个代码单元。
boolean (布尔)类型有两个值:false 和 true,用来判定逻辑条件。整型值和布尔值之间不能进行相互转换。
二.变量1.不能使用 Java 保留字作为变量名。
2.在 Java 中,利用关键字 final 指示常量。关键字 final 表示这个变量只能被赋值一次。一旦被赋值之后,就不能够再更改了。习惯上,
常量名使用全大写。
3.可以使用关键字 static final 设置一个类常量。
三.运算符需要注意, 整数被 0 除将会产生一个异常, 而浮点数被 0 除将会得到无穷大或 NaN 结果。
3.1 数值类型之间的转换1.有 6 个实心箭头,表示无信息丢失的转换;有 3 个虚箭头, 表示可能有精度损失的转换。例如,123456789 是一个大整数, 它所包含的位数比 float 类型所能够表达的位数多。 当将这个整型数值转换为 float 类型时, 将会得到同样大小的结果,但却失去了一定的精度。
int n = 123456789; float f =n; //f is 1.23456792E8
2.当使用上面两个数值进行二元操作时(例如 n + f,n 是整数,f 是浮点数) ,先要将两个操作数转换为同一种类型,然后再进行计算。
- 如果两个操作数中有一个是 double 类型,另一个操作数就会转换为 double 类型。
- 否则,如果其中一个操作数是 float 类型,另一个操作数将会转换为 float 类型。
- 否则,如果其中一个操作数是 long 类型,另一个操作数将会转换为 long 类型。
- 否则,两个操作数都将被转换为 int 类型。
1.在必要的时候,int 类型的值将会自动地转换为 double 类型。但另一方面,有时也需要将 double 转换成 int。在 Java 中,允许进行这种数值之间的类型转换。当然,有可能会丢失一些信息。在这种情况下,需要通过强制类型转换(cast) 实现这个操作。强制类型转换的语法格式是在圆括号中给出想要转换的目标类型,后面紧跟待转换的变量名。
double x = 9.997; //强制类型转换通过截断小数部分将浮点值转换为整型。 int nx = (int) x; //nx=9 //如果想对浮点数进行舍入运算以便得到最接近的整数,那就需要使用Math.round方法 //int nx = (int)Math.round(x); //nx=10
2.警告:如果试图将一个数值从一种类型强制转换为另一种类型,而又超出了目标类型的表示范围,结果就会截断成一个完全不同的值。例如,(byte ) 300 的实际值为 44。
注:byte一字节,int四字节
3.3 结合赋值和运算符1.可以在赋值中使用二元运算符。例如 x+=4;等价于x=x+4;(一般地,要把运算符放在 = 号左边,如 *= 或 %=)。
2.如果运算符得到一个值,其类型与左侧操作数的类型不同,就会发生强制类型转换。例如,如果 X 是一个 int,则以下语句x += 3.5;是合法的,将把 x 设置为(int)(x + 3.5)。
3.4 自增与自减运算符1.int n=12;n++;将 n 的值改为 13。由于这些运算符会改变变量的值,所以它们的操作数不能是数值。例如,4++ 就不是一个合法的语句。
2.n++与++n的区别:前缀形式会先完成加 1; 而后缀形式会使用变量原来的值
int n =1; int x = ++n; System.out.println(x); //2 System.out.println(n); //2 int m =1; int y = m++; System.out.println(y); //1 System.out.println(m); //23.5 关系和 boolean 运算符
1.&& 和 || 运算符是按照"短路"方式来求值的:如果第一个操作数已经能够确定表达式的值,第二个操作数就不必计算了。
2.三元操作符表达式condition ? expression1:expression2。如果条件为 true,就为第一个表达式的值,否则计算为第二个表达式的值。
3.6 位运算符略
3.7 括号与运算符级别1.&& 的优先级比 || 的优先级高,所以表达式a && b || c等价于(a && b)|| c
2.因为 += 是右结合运算符,所以表达式a += b += c等价于a += (b += c),也就是将 b += c 的结果(加上 c 之后的 b) 加到 a 上。
3.下表给出了运算符的优先级。 如果不使用圆括号,就按照给出的运算符优先级次序进行计算。同一个级别的运算符按照从左到右的次序进行计算(除了表中给出的右结合运算符外。)
从概念上讲,Java 字符串就是 Unicode 字符序列。Java 没有内置的字符串类型,而是在标准 Java 类库中提供了一个预定义类,很自然地叫做 String。每个用双引号括起来的字符串都是 String类的一个实例
4.1 拼接1.Java语言允许使用 + 号连接(拼接)两个字符串。
2.当将一个字符串与一个非字符串的值进行拼接时,后者被转换成字符串。
4.2 不可变字符串1.String 类没有提供用于修改字符串的方法。由于不能修改 Java 字符串中的字符, 所以在 Java 文档中将 String 类对象称为不可变字符串
2.不可变字符串有一个优点:编译器可以让字符串共享
4.3 检测字符串是否相等1.一定不要使用==运算符检测两个字符串是否相等!这个运算符只能够确定两个字串是否放置在同一个位置上。当然,如果字符串放置在同一个位置上,它们必然相等。但是,完全有可能将内容相同的多个字符串的拷贝放置在不同的位置上。
2.如果虚拟机始终将相同的字符串共享,就可以使用==运算符检测是否相等。但实际上只有字符串常量是共享的,而 + 或 substring 等操作产生的结果并不是共享的。
4.4 空串与NULL串1.空串 “” 是长度为 0 的字符串。
4.5 码元与代码单元1.Java 字符串由 char 值序列组成。char 数据类型是一个采用 UTF-16 编码表示 Unicode 码点的代码单元。大多数的常用 Unicode 字符使用一个代码单元就可以表示,而辅助字符需要一对代码单元表示。
2.length 方法将返回采用 UTF-16 编码表示的给定字符串所需要的代码单元数量。要想得到实际的长度,即码点数量,可以调用codePointCount方法。调用 s.charAt(n) 将返回位置 n 的代码单元,n 介于 0 ~ s.length()-1 之间。
String hello = "hi핆";
System.out.println(hello.length());// 4
System.out.println(hello.codePointCount(0, hello.length()));// 3
for (int i = 0; i < hello.length(); i++) {
char charAt = hello.charAt(i);
// 68 69 d835 d835
System.out.println(Integer.toHexString(charAt));
}
for (int i = 0; i < hello.codePointCount(0, hello.length()); i++) {
// 得到第i个码点
int index = hello.offsetByCodePoints(0, i);
int charAt = hello.codePointAt(index);
// 68 69 1d546
System.out.println(Integer.toHexString(charAt));
}
3.使用 UTF-16 编码表示字符핆(U+1D546) 需要两个代码单元。
4.6 构建字符串1.StringBuilder 类的前身是 StringBuffer,其效率稍有些低,但允许采用多线程的方式执行添加或删除字符的操作。如果所有字符串在一个单线程中编辑(通常都是这样) ,则应该用 StringBuilder 替代它。
4.7 格式化输出1.可以使用静态的 String.format 方法创建一个格式化的字符串
String format = String.format("Hello, %s. Next year, you'll be %d", "tom", 18);
System.out.println(format);
五.字符串常量池常见问题(非书内内容,参考JavaGuide)
5.1 String 类型的变量和常量做"+"运算时发生了什么?
1.代码如下:
String str1 = "str"; String str2 = "ing"; String str3 = "str" + "ing";//常量池中的对象 String str4 = str1 + str2; //在堆上创建的新的对象 String str5 = "string";//常量池中的对象 // System.out.println(str3 == str4);//false // System.out.println(str3 == str5);//true // System.out.println(str4 == str5);//false
2.对字节码文件(.class文件)进行反汇编:javap -c Test.class > Text.txt,详情参见Java 虚拟机指令集
3.解释:
(1)对于编译期可以确定值的字符串,也就是常量字符串,jvm 会将其存入字符串常量池。字符串常量池是 JVM 为了提升性能和减少内存消耗针为字符串(String 类)专门开辟的一块区域,主要目的是为了避免字符串的重复创建。
(2)字符串常量拼接得到的字符串常量在编译阶段就已经被存放字符串常量池,这个得益于编译器的优化。
(3)对象引用和"+"的字符串拼接方式,实际上是通过 StringBuilder 调用 append() 方法实现的,拼接完成之后调用 toString() 得到一个 String 对象 。因此,str4 并不是字符串常量池中存在的对象,属于堆上的新对象。
String str4 = new StringBuilder().append(str1).append(str2).toString();
(4)被 final 关键字修改之后的 String 会被编译器当做常量来处理,编译器在程序编译期就可以确定它的值,其效果就相当于访问常量。如果 ,编译器在运行时才能知道其确切值的话,就无法对其优化。
final String str1 = "str"; final String str2 = "ing"; // 下面两个表达式其实是等价的 String c = "str" + "ing";// 常量池中的对象 String d = str1 + str2; // 常量池中的对象 System.out.println(c == d);// true
//str2 在运行时才能确定其值
final String str1 = "str";
final String str2 = getStr();
String c = "str" + "ing";// 常量池中的对象
String d = str1 + str2; // 在堆上创建的新的对象
System.out.println(c == d);// false
public static String getStr() {
return "ing";
}
5.2 String s1 = new String(“abc”);这句话创建了几个字符串对象?
会创建 1 或 2 个字符串: 如果字符串常量池中已存在字符串常量“abc”,则只会在堆空间创建一个字符串常量“abc”。 如果字符串常量池中没有字符串常量“abc”,那么它将首先在字符串常量池中创建,然后在堆空间中创建,因此将创建总共 2 个字符串对象。
String s1 = new String("abc");// 堆内存的地址值
String s2 = "abc";//指向的是字符串常量池的对象
System.out.println(s1 == s2);// 输出 false,因为一个是堆内存,一个是常量池的内存,故两者是不同的。
六.控制流程
6.1 块作用域
1.不能在嵌套的两个块中声明同名的变量。
1.当条件为 true 时,while 循环执行一条语句(也可以是一个语句块)。一般格式为while ( condition ) statement。如果开始循环条件的值就为 false, 则 while 循环体一次也不执行。
2.do statement while ( condition);这种循环语句先执行语句(通常是一个语句块) ,再检测循环条件;然后重复语句再检测循环条件,以此类推。
3.for 循环语句
for (int i = 1; i <= 10; i++) {
System.out.println(i);
}
4.多重选择:switch 语句
(1)switch语句将从与选项值相匹配的 case 标签处开始执行直到遇到 break 语句,或者执行到 switch 语句的结束处为止。如果没有相匹配的 case 标签, 而有 default 子句, 就执行这个子句。
(2)如果在 case 分支语句的末尾没有 break 语句,那么就会接着执行下一个 case 分支语句。这种情况相当危险,常常会引发错误。
(3)case 标签可以是:
- 类型为 char、byte、 short 或 int 的常量表达式
- 枚举常量
- case 标签还可以是字符串字面量。
5.中断控制流程语句
(1)不带标签的break语句,用于退出循环语句
(2)带标签的break,用于跳出多重嵌套的循环语句,跳到嵌套的所有循环语句之外。请注意,标签必须放在希望跳出的最外层循环之前, 并且必须紧跟一个冒号。
outer: for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
if (j == 5) {
System.out.println(j);
break outer;
}
}
}
(3)continue语句越过了当前循环体的剩余部分,立刻跳到循环首部。
(4)带标签的 continue 语句,将跳到与标签匹配的循环首部。
outer: for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
if (j == 5) {
System.out.println(j);
continue outer;
}
}
}
七.大数值
1.如果基本的整数和浮点数精度不能够满足需求,那么可以使用 java.math 包中的两个很有用的类:Biglnteger 和 BigDecimal。这两个类可以处理包含任意长度数字序列的数值。Biglnteger 类实现了任意精度的整数运算,BigDecimal 实现了任意精度的浮点数运算。
2.使用静态的 valueOf方法可以将普通的数值转换为大数值
3.不能使用人们熟悉的算术运算符(如:+ 和 *) 处理大数值。 而需要使用大数值类中的 add 和 multiply 方法。
八.数组1.数组是一种数据结构, 用来存储同一类型值的集合。通过一个整型下标可以访问数组中的每一个值。在声明数组变量时,需要指出数组类型 ( 数据元素类型紧跟 []) 和数组变量的名字,比如int[] a;这条语句只声明了变量 a,并没有将 a 初始化为一个真正的数组。应该使用 new 运算符创建数组。int[] a = new int[100];这条语句创建了一个可以存储 100 个整数的数组。这个数组的下标从 0 ~ 99 (不是 1 ~ 100 )。
2.创建一个数字数组时,所有元素都初始化为 0。boolean 数组的元素会初始化为 false。对象数组的元素则初始化为一个特殊值 null,这表示这些元素(还)未存放任何对象。
3.一旦创建了数组, 就不能再改变它的大小(尽管可以改变每一个数组元素)。如果经常需要在运行过程中扩展数组的大小, 就应该使用另一种数据结构—数组列表( array list)。
8.1 for each 循环1.语句格式:for (variable : collection) statement。collection 这一集合表达式必须是一个数组或者是一个实现了 Iterable 接口的类对象。
8.2 数组初始化以及匿名数组1.在 Java中, 提供了一种创建数组对象并同时赋予初始值的简化书写形式。int[] b = {2,3,4,5,6};
2.初始化一个匿名的数组:new int[] {2,3,4,5,6};
8.3 数组拷贝1.在 Java 中,允许将一个数组变量拷贝给另一个数组变量。这时, 两个变量将引用同一个数组
int[] smallPrimes = { 2, 3, 5, 7, 11, 13 };
int[] luckyNumbers = smallPrimes;
luckyNumbers[5] = 12;
System.out.println(smallPrimes[5]);
8.4 命令行参数
1.每一个 Java 应用程序都有一个带 String arg[]参数的 main 方法。这个参数表明 main 方法将接收一个字符串数组,也就是命令行参数。
public class Test {
public static void main(String[] args) {
if (args.length == 0 || args[0].equals("-h")) {
System.out.print("Hello,");
} else if (args[0].equals("-g")) {
System.out.print("Goodbye,");
for (int i = 1; i < args.length; i++) {
System.out.print(" " + args[i]);
}
System.out.println("!");
}
}
}
8.5 数组排序
要想对数值型数组进行排序, 可以使用 Arrays 类中的 sort 方法
8.6 不规则数组1.Java 实际上没有多维数组,只有一维数组。多维数组被解释为"数组的数组"
2.比如一个二维数组balances,balances 数组实际上是一个包含 10 个元素的数组,而每个元素又是一个由 6 个浮点数组成的数组
3.还可以方便地构造一个"不规则" 数组, 即数组的每一行有不同的长度。



