Java核心技术卷I重点摘录


感觉这本书后面写的像是API介绍书,难以看进去,前面之前看的又忘了,Java基础面试又容易问,于是快速回顾下,摘录重点(部分),方便回忆。

Java的基本程序设计结构

数据类型

整型

在Java中,整型的范围与运行Java代码的机器无关。这就解决了软件从一个平台到另一个平台,或者在同一个平台中不同操作系统之间进行移植的诸多问题。与此相反,C和C++程序需要针对不同的处理器选择最为高效的整型。

变量

final

在Java中,利用关键字final指示常量。关键字final表示这个变量只能被赋值一次,一旦被赋值之后,就不能够再更改了。
在Java中,经常希望某个常量可以在一个类的多个方法中使用,通常将这些常量成为类常量。可以使用关键字static final设置一个类常量,定义于main方法的外部。

运算符

  1. 整数被0除将会产生一个异常,而浮点数被0除将会得到无穷大或NaN结果。
  2. int->float,long->float,long->double数值之间转换可能会有精度损失。
  3. 强制类型转换可能会丢失一些信息。
  4. &&和||运算符是按照“短路”方法来求值的:如果第一个操作数已经能确定表达式的值,第二个操作数就不必计算了。
  5. 应用在boolean值上时,&和|运算符也会得到一个布尔值。这些运算符于&&和||运算符很类似,不过它们不采用“短路”的方法求值,也就是说,得到计算结果之前的两个操作数都需要计算。
  6. 运算符用0填充高位。

  7. 在C/C++中,不能保证>>是完成算是有移位(扩展符号位)还是逻辑移位(填充0),java消除了这种不确定性。
  8. 与C/C++不同,Java不使用都好运算符,不过可以分隔用。
  9. Java没有提供运算符虫灾功能。

字符串

  1. String类没有提供用于修改字符串的方法,String类对象是不可变字符串。修改string实际上是创建了一个新字符串。
  2. 不可变字符串有一个有点:编译器可以让字符串共享。
  3. C++字符串可以修改
  4. String是被final修饰的类,不能继承
  5. 可以用equals方法检测两个字符串是否相等,而==运算符只能确定两个字符串是否放置在同一个位置上。
  6. 空串是一个java对象,有自己的串长度(0)和内容(空)。不过String变量还可以存放一个特殊的值,名为null,这表示目前没有任何对象与该变量关联。
  7. Java字符串由char值序列组成
  8. StringBuilder的前身是StringBuffer,其效率稍低,但允许采用多线程的方式执行添加或删除字符的操作。如果所有字符串在一个单线程中编辑(通常都是这样),则应该用StringBuilder替代它,这两个类的API是相同的。

控制流程

  1. goto可以跳出多重循环。Java还提供了一种带标签的break语句,用于跳出多重嵌套的循环语句。并不提倡使用。

数组

  1. 创建一个数字数组时,所有元素都初始化为0,boolean数组的元素初始化为false,对象数组的元素则初始化为一个特殊值null。
  2. 一旦创建数组,就不能再改变它的大小(尽管可以改变每一个数组元素),如果经常需要再运行过程中扩展数组的大小,则应使用数组列表。
  3. 匿名数组可以再不创建新变量的情况下重新初始化一个数组。
  4. java允许数组长度为0.
  5. for each循环语句不能自动处理二位数组的每一个元素。它是按照行,也就是一维数组处理的。要想访问二维数组a的所有元素,需要使用两个嵌套的循环。
  6. Java实际上没有多维数组,只有一维数组,多维数组被解释为“数组的数组”。

对象与类

  1. 类之间的关系:依赖、聚合、继承。
  2. 构造器与其他的方法有一个重要的不同。构造器总是伴随着new操作符的执行被调用,而不能对一个已经存在的对象调用构造器来重新设置实例域的目的。
  3. Java中所有方法都必须在类的内部定义,C++通常在外部。
  4. 封装的优点:可以改变内部实现,除了该类的方法外,不影响其他代码;更改器方法可以执行错误检查,然而直接对域进行赋值讲不会进行这些处理。
  5. 可以将实例域定义为final,构建对象时必须初始化这样的域。也就是说,必须确保在每一个构造器执行之后,这个域的值将被设置,并且在后面的操作中,不能够再对它进行修改。
  6. final修饰符大都应用于基本类型域,或不可变类的域(如果类中的每个方法都不会改变其对象,这种类就是不可变的类,如String类)。
  7. 如果将域定义为static,每个类中只有一个这样的域 ,而每一个对象对于所有的实例域却有自己的一份拷贝。如,假定需要给每一个雇员赋予唯一的标识码,这里给Employee类添加一个实例域id和一个静态域nextId。
calss Employee{
	private static int nextId = 1;
	private int id;
	...
}

现在每个雇员对象都有一个自己的id域,但这个类的所有实例将共享一个nextId域。换句话说,如果有1000个Employee类的对象,则有1000个实例域id,但是,只有1个静态域nextId。即使没有一个雇员对象,静态域nextId也存在。它属于类,而不属于任何独立的对象。
8. 静态方法是一种不能向对象实施操作的方法。 例如,Math类的pow方法就是一个静态方法,在运算时,不适用任何Math对象,换句话说,没有隐式的参数。可以认为静态方法是没有this参数的方法(在一个非静态的方法中,this参数表示这个方法的隐式参数)。
9. 在下面两种情况下使用静态方法:一个方法不需要访问对象状态,其所需参数都是通过显式参数提供;一个方法只需要访问类的静态域。
10. 使用静态工厂方法而不利用构造器原因:无法命名构造器;当使用构造器时,无法改变所构造的对象类型。
11. main方法不对任何对象进行操作。事实上,在启动程序时还没有任何一个对象。静态的main方法将执行并创建程序所需要的对象。
12. 按值调用表示方法接收的是调用者提供的值。按引用调用表示方法接收的是调用者提供的变量地址。一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值。Java总是采用按值调用,也就是说,方法得到的是所有参数值的一个拷贝,特别是,方法不能修改传递给它的任何参数变量的内容。
13. 一个方法不可能修改一个基本数据类型的参数。 而对象引用作为参数就不同了,方法得到的是对象引用的拷贝,对象引用及其他的拷贝同时引用同一个对象。一个方法可以改变一个对象参数的状态。一个方法不能让对象参数引用一个新的对象。
14. 如果构造器的第一个语句形如this(…),这个构造器将调用同一个类的另一个构造器。采用这种方式使用this关键字非常有用,这样对公共的构造器代码部分只编写一次即可。
15. Java有自动的垃圾回收器,不需要人工回收内存,所以Java不支持析构器。
16. 可以为任何一个类添加finalize方法,finalize方法将在垃圾回收器清除对象之前调用。
17. 如果某个资源需要在使用完毕后立刻被关闭,那么就需要由人工来管理。对象用完时,可以应用一个close方法来完成相应的清理操作。

继承

  1. 不允许扩展的类被成为final类,使用final修饰符。类中的特定方法也可以被声明为final,如果这样做,子类就不能覆盖这个方法(final类中的所有方法自动成为final方法,但是不包括域)。对于final域,构造对象之后就不允许改变它们的值了。将方法或类声明为final的主要目的是:确保它们不会再子类中改变语义。
  2. 将一个子类的引用赋给一个超类变量,编译器是允许的。但将一个超类的引用赋给一个子类变量,必须进行类型转换。
  3. 只能在继承层次内进行类型转换;在将超类转换成子类之前,应该使用instanceof进行检查。
  4. 扩展抽象类可以有两种选择。一种是在抽象类中定义部分抽象类方法或不定义抽象类方法,这样就必须将子类也标记为抽象类;另一种是定义全部的抽象方法,这样一来,子类就不是抽象的了。
  5. 类即使不含抽象方法,也可以将类声明为抽象类。抽象类不能被实例化。也就是说,如果将一个类声明为abstract,就不能创建这个类的对象。
  6. Object类中的equals方法用于检测一个对象是否等于另外一个对象。在Object类中,这个方法将判断两个对象是否具有相同的引用。如果两个对象具有相同的引用,它们一定是相等的。
  7. 由于hashCode方法定义在Object类中,因此每个对象都有一个默认的散列码,其值为对象的存储地址。在StringBuffer类中没有定义hashCode方法,它的散列码是由Object类的默认hashCode方法导出的对象存储地址。如果重新定义equals方法,就必须重新定义hashCode方法,以便用户可以将对象插入到散列表中。Equals与hashCode的定义必须一致:如果x.equals(y)返回true,那么x.hashCode()就必须与y.hashCode()具有相同的值。
  8. 所有的基本类型都有一个与之对应的类。例如,Integer类对应基本类型int。通常,这些类称为包装器(wrapper)。这些对象包装器类拥有很明显的名字:Integer、Long、Float、Double、Short、Byte、Character、Void和Boolean(前6个类派生于公共的超类Number)。对象包装器类是不可变的,即一旦构造了包装器,就不允许更改包装在其中的值。同时,对象包装器类还是final,因此不能定义它们的子类。
  9. 自动装箱、自动拆箱
  10. 所有枚举类型都是Enum类的子类。每个枚举类都有一个静态的values方法,它将反汇一个包含全部枚举值的数组。
  11. 反射是程序运行期间JVM会对任意一个类洞悉它的属性和方法,对任意一个对象都能够访问它的属性和方法。依靠此机制,可以动态的创建一个类的对象和调用对象的方法。其次就是反射相关的API,只讲一些常用的,比如获取一个Class对象,Class.forName(完整类名)。通过Class对象获取类的构造方法,class.getConstructor。根据class对象获取类的方法,getMethod和getMethods。使用class对象创建一个对象,class.newInstance等。最后可以说一下反射的优点和缺点,优点就是增加灵活性,可以在运行时动态获取对象实例。缺点是反射的效率很低,而且会破坏封装,通过反射可以访问类的私有方法,不安全。
  12. 继承的设计技巧:将公共操作和域放在超类;不要使用受保护的域(不过,protected方法对于指示那些不提供一般用途而应在子类中重新定义的方法很有用);使用继承实现“is-a”关系;除非所有继承的方法都有意义,否则不要使用继承;在覆盖方法时,不要改变预期的行为;使用多态,而非类型信息;不要过多地使用反射。

接口、lamda表达式与内部类

接口

  1. 在Java中,接口不是类,而是对类地一组需求描述。接口所有方法自动地属于public。
  2. 接口绝不能含有实例域。提供实例域和方法实现的任务应该由实现接口的那个类来完成。
  3. 让一个类排序必须让它实现compareTo方法,但是为什么不在类中直接提供一个compareTo方法,而必须实现Comparable接口?因为Java是一种强类型语言,在调用方法时,编译器将会检查这个方法是否存在。为此,编译器必须确认一定有compareTo方法,而每个实现Comparable接口的类都必须提供这个方法的定义。
  4. 不能构造接口的对象,但能声明接口的变量,接口对象必须引用实现了接口的类对象。
  5. 使用抽象类表示通用属性存在这样一个问题:每个类只能扩展于一个类。
  6. 接口可以提供多重继承的大多数好处,同时还能避免多重继承的复杂性和低效性。
  7. 默认方法可以调用任何其他方法。
  8. 默认方法的一个重要用法是“接口演化”(interface evolution)。为接口增加一个非默认方法不能保证“源代码兼容”。
  9. 解决默认方法冲突:1)超类优先。如果超类提供了一个具体方法,同名而且有相同参数类型的默认方法会被忽略。2)接口冲突。如果一个超接口提供了一个默认方法,另一个接口提供了一个同名而且参数类型(不论是否是默认参数)相同的方法,必须覆盖这个方法来解决冲突。
  10. 默认的克隆操作是“浅拷贝”,并没有克隆对象中引用的其他对象。浅拷贝会有什么影响吗?这要看具体情况。如果原对象和浅克隆对象共享的子对象是不可变的,那么这种共享就是安全的。如果子对象属于一个不可变的类,如String,就是这种情况。或者在对象的生命期中,子对象一直包含不变的常量,没有更改器方法会改变它,也没有方法会生成它的引用,这种情况下同样是安全的。
  11. 不过,通常子对象都是可变的,必须重新定义clone方法来建立一个深拷贝,同时克隆所有子对象。

lambda表达式

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值