Xiayf的技术笔记

《Java核心技术(卷1)》读书笔记

声明一个变量之后,必须用赋值语句对变量进行显式初始化,千万不要使用未被初始化的变量。

在Java中,利用关键字final声明常量。关键字final表示这个变量只能被赋值一次。一旦赋值之后,就不能够再更改了。习惯上,常量名使用大写。

从概念上讲,Java字符串就是Unicode字符序列。Java没有内置的字符串类型,而是在标准Java类库中提供了一个预定义类String。

String类没有提供用于修改字符串的方法。String类对象是不可变字符串。

可以使用s转换符格式化任意的对象。对于任意实现了Formattable接口的对象都将调用formatTo方法;否则将调用toString方法,它可以将对象转换为字符串。

可以使用静态的String.format方法创建一个格式化的字符串,而不打印输出:

String message = String.format("Hello, %s. Next year, you'll be %d", name, age);

要想对文件进行读取,就需要用一个File对象构造一个Scanner对象,如下所示:

Scanner in = new Scanner(new File("myfile.txt"));

要想写入文件,就需要构造一个PrintWriter对象。在构造器中,只需要提供文件名,如果文件不存在,创建该文件:

PrintWriter out = new PrintWriter("myfile.txt");

Java中,允许将一个数组变量赋值给另一个数组变量。这时,两个变量将引用同一个数组。如果希望将一个数组的所有值拷贝到一个新的数组中去,就要使用Arrays类的copyTo方法:

int[] copiedLuckyNumbers = Arrays.copyTo(luckyNumbers, luckyNumbers.length);

一个对象变量并没有实际包含一个对象,而仅仅引用一个对象。

在Java中,任何对象变量的值都是对存储在另外一个地方的一个对象的引用。new操作符的返回值也是一个引用。

下列语句:

Date deadline = new Date();

有两个部分:表达式new Date()构造了一个Date类型的对象,并且它的值是对新创建对象的引用。这个引用存储在变量dealine中。

可以显式地将对象变量设置为null,表明这个对象变量目前没有引用任何对象。

所有的Java对象都存储在堆中。

  • 构造器与类同名
  • 每个类可以有一个以上的构造器
  • 构造器可以有0个、1个或1个以上的参数
  • 构造器没有返回值
  • 构造器总是伴随着new操作一起调用

注意不要编写返回引用可变对象的访问器方法。如果需要返回一个可变对象的引用,应该首先对它进行克隆。凭经验可知,如果需要返回一个可变数据域的拷贝,就应该使用克隆。

可以将类的数据域定义为final。对象构建时必须初始化这样的域。也就是说,必须确保在每个构造器执行之后,这个域的值被设置,并且在后面的操作中,不能够再对它进行修改。

final修饰符大都应用于基本数据(primitive)类型域,或不可变(immutable)类的域(如果类中每个方法都不会改变其对象,这种类就是不可变的类。)

可以认为静态方法是没有this参数的方法(在一个非静态的方法中,this参数标识这个方法的隐式参数)。因为静态方法不能操作对象,所以不能在静态方法中访问实例域。但是静态方法可以访问自身类中的静态域。

在下面两种情况下使用静态方法:

  • 一个方法不需要访问对象状态,其所需参数都是通过显式参数提供
  • 一个方法只需要访问类的静态域

值调用(call by value)表示方法接收的是调用者提供的值。而引用调用(call by reference)表示方法接收的是调用者提供的变量地址。一个方法可以修改传递引用调用所对应的变量值,而不能修改传递值调用对应的变量值。

Java总是采用值调用。也就是说,方法得到的是所有参数值的一个拷贝,特别是,方法不能修改传递给它的任何参数变量的内容。

方法参数共有两种类型:

  • 基本数据类型
  • 对象引用

基本数据类型的变量值传递拷贝的是值本身,但对象引用值拷贝的是引用,不是引用指向的对象。

Java允许重载任何方法,而不只是构造器方法。因此,要完整地描述一个方法,需要指出方法名以及参数类型。这叫做方法的签名(signature)。返回类型不是方法签名的一部分。

如果在构造器中没有显式地给域赋予初值,那么就会被自动地赋为默认值:数值为0、布尔值为false、对象引用为null。

对象域与局部变量的主要不同点:必须明确地初始化方法中的局部变量。但是,如果没有初始化类中的域,将会被初始化未默认值(0、false或null)。

如果在编写一个类时没有编写构造器,那么系统就会提供一个默认构造器。这个默认构造器将所有的实例域设置为默认值。

类设计技巧:

  1. 一定要将数据设计为私有
  2. 一定要对数据初始化
  3. 不要在类中使用过多的基本数据类型 - 就是说,用其他的类代替多个相关的基本数据类型的使用。
  4. 不是所有的域都需要独立的域访问器和域更改器
  5. 将职责过多的类进行分解
  6. 类名和方法名要能够体现它们的职责

有时候,可能希望阻止人们利用某个类定义子类。不允许扩展的类被成为final类。如果在定义类的时候使用了final修饰符就表明这个类是final类。

类中的方法也可以被声明为final。如果这样做,子类就不能覆盖这个方法(final类中的所有方法自动地成为final方法)。

Object类是Java中所有类的最终祖先,在Java中每个类都是由它扩展而来的。但是并不需要这样写:

class Employee extends Object

如果没有明确地指出超类,Object就被认为是这个类的超类。

在Java中,只有基本类型(primitive types)不是对象,例如:数值、字符和布尔类型的值都不是对象。所有数组类型,不管是对象数组还是基本类型的数组都扩展于Object类。

Object类中的equals方法用于检测一个对象是否等于另外一个对象。在Object类中,这个方法将判断两个对象是否具有相同的引用。如果两个对象具有相同的引用,它们一定是相等的。

如果重新定义equals方法,就必须重新定义hashCode方法,以便用户可以将对象插入到散列表中。

强烈建议为自定义的每一个类增加toString方法。

在程序运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识。这个信息保存着每个对象所属的类足迹。虚拟机利用运行时信息选择相应的方法执行。

将forName与newInstance配合起来使用,可以根据存储在字符串中的类名创建一个对象。

String s = "java.util.Date";
Object m = Class.forName(s).newInstance();

接口中的所有方法自动地属于public。因此,在接口中声明方法时,不必提供关键字public。

内部类(inner class)是定义在另一个类中类。为什么需要使用内部类呢?其主要原因有以下三点:

  • 内部类方法可以访问该类定义所在的作用域中数据,包括私有的数据
  • 内部类可以对同一个包中的其他类隐藏起来
  • 当想要定义一个回调函数且不想编写大量代码时,使用匿名(anonymous)内部类比较便捷

有时候,使用内部类只是为了把一个类隐藏在另一个类的内部,并不需要内部类引用外围类对象。为此,可以将内部类声明未static,以便取消产生的引用。

断言机制允许在测试期间向代码中插入一些检测语句。当代码发布时,这些插入的检测语句将会被自动地移走。

在默认情况下,断言被禁用。可以在运行程序时用-enableassertions或-ea选项启用它:

java -enableassertions MyApp

需要注意:在启用或禁用断言时不必重新编译程序。启用或禁用断言是类加载器(class loader)的功能。当断言被禁用时,类加载器将跳过断言代码,因此,不会降低程序运行的速度。

在Java语言中,给出了三种处理系统错误的机制:

  • 抛出一个异常
  • 日志
  • 使用断言

什么时候应该选择断言呢?请记住下面几点:

  • 断言失败是致命的、不可恢复的错误
  • 断言检查只用于开发和测试阶段