Java核心技术(卷1)
一、基础概念
1.1 基本程序设计结构
-
1.1 数据类型
-
1.1.1 数值类型
-
1️⃣从java7开始,加上前缀0b或0B就可以写二进制;
-
2️⃣指数的表示
-
十进制中以10为底指数的表示:
- double d = 1.0e+4; // 10000.0
- double d2 = 100000.0e-4; // 10.0
-
十六进制中以2位底指数的表示:
- double a1 = 0x1.0p+3; // 8.0
- double a2 = 0x1.0p-3; // 0.125
-
-
3️⃣double中的严格浮点计算:strictfp
- double类型使用64位存储一个数值,而有些处理器使用80位浮点寄存器。这些寄存器增加了中间过程的计算精度(将中间结果存储在80位寄存器中,将最终结果截断位64位)。这个过程增加了计算精度,但结果可能与始终在64位机器上计算的结果不一样。
- 如果将一个类标记为strictfp,那么这个类的所有方法都要使用严格的浮点计算。
- 严格浮点计算:所有的中间计算必须进行截断。
- 默认情况下是不采用严格的浮点计算。
-
double类型的三个特殊值:增无穷大、负无穷大、NaN
-
Double.POSITIVE_INFINITY // 正无穷大
-
Double.NEGATIVE_INFINITY // 负无穷大
-
Double.NaN
- 检查一个字符是否为数值:Double.isNaN(x)
-
-
-
1.1.2 Unicode和char类型
-
Unicode
-
专业术语
- 码点:某个字符所对应的代码值;
- Unicode中码点的表示:Unicode的码点可以分为17个级别,第一个级别,码点从U+0000到U+FFFF,被称为基本的多语言级别,其中包括经典的Unicode代码;其余的16个级别的码点从U+10000到U+10FFFF,其中包括一些辅助字符。
- 代码单元:用8位或16位(UTF-8是8位表示,UTF-16用16位表示)字节来表示Unicode中基本的多语言级别,被称之为代码单元。也就是说,UTF-8中代码单元是8位字节,UTF-16中代码单元是16位字节。
-
UTF-16编码采用不同长度的编码表示所有的Unicode码点
- 在UTF-16中,表示一个Unicode码点,需要一个或两个代码单元。
-
-
char类型
-
在java中,char类型描述了UTF-16的一个代码单元
- 因此,“char用来表示单个字符”的说法不完全正确。
-
char类型是用16位表示
-
有些unicode字符可以用一个char值来描述,另一些Unicode字符需要用两个char值来描述。
-
-
区分码点、代码单元的用途:
-
length() 方法
- length方法将返回UTF-16编码表示的给定字符串所需要的代码单元数量。
-
想要获取实际长度,即码点数量,即字符个数
- String greeting = “hello”;
int cpCount = greeting.codePointCount(0, greeting.length());
- String greeting = “hello”;
-
获取第几个代码单元:
- String greeting = “hello”;
char first = greeting.atChar(0);
- String greeting = “hello”;
-
获取第几个码点:
- String greeting = “hello”;
// i 为第i个码点,i从0开始
int index = greeting.offsetByCodePoints(0, i);
greeting.codePointAt(index);
- String greeting = “hello”;
-
-
-
1.1.3 大数值: BigInteger和BigDecimal
- BigInteger可以实现任意精度的整数运算;
BigDecimal可以实现任意精度的浮点运算;
- BigInteger可以实现任意精度的整数运算;
-
-
1.2 规范
-
1.2.1 变量
-
关于“$”
- 尽管$是一个合法的Java字符,但不要在自己的编码中使用这个字符,它只用在Java编译器或其他工具生成的名字中。
-
关于“++”的使用
- 建议不要在代码中使用“++”,这样的代码很容易让人困惑,而且会带来烦人的bug。
-
-
-
1.3 运算符
-
1.3.1 位运算符
-
和<<运算符将位模式左移或右移
-
运算符会用0填充高位;>> 会用符号位填充高位;不存在<<< 运算符;
- 位运算符的右操作数要完成模32的运算(除非左操作数是long类型,这种情况下需要对右操作数模64):1<<35 等于1<<3 等于8
-
-
1.3.2 运算符的级别
- &&的优先级比|| 的优先级高
- += 是右结合运算符,所以表达式: a+=b +=c 等价于: a+=(b+=c)
-
-
1.4 String
-
1.1 String是不可变字符串。一定不要使用“==”检测两个字符串是否相等,每次连接字符串,都会构建一个新的String对象;
每次连接都会创建一个新的字符串,这样既耗时,又浪费空间。使用StringBuilder类就可以避免这个问题的发生。
StringBuilder的前身是StringBuffer。StringBuffer效率稍微有些低,但允许采用多线程的方式执行添加或删除字符的操作。
StringBuilder 除了能够apped字符串,还能添加代码单元,添加码点
所有字符串都在一个单线程中编辑,则应该用StringBuilder代替它。
- 编译器可以让字符串共享。如果虚拟机使用让字符串共享就可以使用“==”运算符检测两个字符串是否相等。但,实际上只是字符串常量可以共享,而+或substring等操作产生的结果并不共享。
- “==”运算符能够用来确定两个字符串是否放在同一个位置。如果两个字符串放在同一个位置,它们必然相等。完全有可能将相同内容的字符串放在不同的位置上。
- 使用equals 方法比较两个字符串是否相等。
-
1.2 String 的length方法将返回采用UTF-16编码表示的给定字符所需要的代码单元数量
-
要想获取实际的字符长度,即码点数量,可以调用:codePointCount 方法
-
public static void test04() throws UnsupportedEncodingException {
String greeting = “Hello”;
String codeStr = “\uD835\uDD46”;
byte[] b = codeStr.getBytes(“UTF-16”);
String s = new String(b, “UTF-16”);
System.out.println(“greeting:”+greeting);
System.out.println(“一个UTF-16编码的特殊符号s:”+s);
System.out.println("greeting.length() : " + greeting.length()); // 打印出 5
System.out.println("greeting.codePointCount() : " + greeting.codePointCount(0, greeting.length())); // 打印出5
System.out.println("s.length() : " + s.length()); // 打印出 2
System.out.println("s.codePointCount() : " + s.codePointCount(0, s.length())); // 打印出 1
}- greeting:Hello
一个UTF-16编码的特殊符号s:?
greeting.length() : 5
greeting.codePointCount() : 5
s.length() : 2
s.codePointCount() : 1
- greeting:Hello
-
-
1.3 连接字符串:join 方法,可以指定分界符
-
1.4 所有字符串都属于CharSequence接口
-
-
1.5 控制流程
- “break 和 跳出标签”的用法:可以将标签应用到任何语句中,甚至可以应用到if语句或块语句中
-
1.6 数组
1.2 对象和类
-
类
-
在一个源文件中,只能有一个共有类,但可以有任意数目的非公有类。
-
构造器
- 构造器与类同名
-
finalize方法
- 可以为任何一个类添加finalize方法。finalize方法将在垃圾回收器清楚对象之前调用(但是很难知道这个方法什么时候才能被调用)。
-
-
对象
-
Java的对象都是在堆中创造;
-
⚠️警告:不要编写返回引用可变对象的访问器方法。如果需要返回一个可变对象的引用,应该首先对它进行克隆(clone)。
- // 错误代码示例
/**
- // 错误代码示例
-
-
Date对象有一个更改器方法setTime。
-
也就是说Date对象是可变的,这一点就破坏了
-
封装性!
*/
public Date gethireDay(){
return this.hireDay;
}
- // 正确的做法
public Date getHireDay(){
return (Date)this.hireDay.clone();
}-
封装:
-
封装通常提供三项内容:
- 一个私有的数据域;
- 一个公有的域访问器方法;
- 一个公有的域更改器方法;
-
封装的好处:
- 1️⃣可以改变内部实现;
- 2️⃣更改器方法可以执行错误检查;
-
警告:永远不要编写返回引用 可变对象 的方法器方法。
- 如果要返回一个可变对象的引用,首先对其进行克隆,否则将破坏封装性;
- 例如:
LocalDate类没有更改器方法;
Date对象是可变的
-
-
调用构造器的具体处理步骤:
-
0、静态域在类被加载的时候进行初始化。静态代码块在类第一次的时候执行一次。(创建对象的时候不再执行)
-
1、所有数据域被初始化为默认值(0、false、或null);
-
2、按照在类声明中出现的顺序,依次执行所有域初始化语句和初始化块;
- 对于类的静态域进行初始化的代码比较复杂,可以使用静态的初始化块;
-
3、如果构造器第一行调用了第二个构造器,则执行第二个构造器主体;
-
4、执行这个构造器的主体;
-
-
-
修饰符:
-
final 实例域
- 可以将实例域声明为final,在构造对象的时候必出初始化这样的域;
- final修饰符大都应用于基本(primitive)类型域,或不可变(immutable)类的域。对于可变的类,使用final修饰可能会对读着造成混乱
- final 修饰的类不允许被继承;final修饰的方法不允许被覆盖;
- 如果将一个类声明为final,只有其中的方法自动地成为final,而不包括域;
-
static 方法
- 建议使用类名而不是对象名来调用静态方法,因为静态方法和对象没有任何关系。
- 静态方法的另一种常见用途是使用静态工厂方法来构造对象。
-
-
静态导入(import)
- import语句不仅可以导入类,还增加了导入静态方法和静态域的功能
-
其他