Java前四章笔记

CH1

文档

参考书:Java编程思想(英文版第 4版) Java 核心技术(英文版第 9版)

补充知识:JavaEE 数据库 软件设计 Java虚拟机原理 Leetcode刷题

.java -> (编译器) -> .class -> (Java虚拟机) -> 机器码程序

编译:javac HelloWorldApp.java

运行:java HelloWorldApp arg1 arg2

JRE 只包含虚拟机,没有编译器

文档型注释:/* documentation /

main方法

public static void main(String[] args)

  • 以上写法只是通常用的:public和static的位置可以交换;args 可以任意命名,如argv;
  • 作为程序入口,必须是public 才能在任意地方被访问到
  • 由于在启动程序时还没有任何对象,所以只能static方法——无需使用对象就可以调用静态方法
  • 每个类可以有一个main方法,这是一个常用于对类进行单元测试的技巧。当该类所属的应用程序的main方法被执行时,该类本身的main将不会被执行

一些细节

Java不只是一种语言,而是一个包含各种可重用代码的库以及安全性、可移植性、垃圾回收机制的平台

相比于C++,java没有头文件、指针运算、结构、联合、操作符重载、虚基类等。

程序设计语言的成功更多取决于其支撑系统的能力,而不是优美的语法。

访问修饰符用于控制程序的其他部分对这段代码的访问级别。

将类作为一个加载程序逻辑的容器,程序逻辑定义了应用程序的行为。Java中的全部内容都必须放在类中,所有的函数都属于某个类的方法,因此main方法必须有一个外壳类而且必须是静态的。

类名采用驼峰命名法,且文件名要与公共类名相同。

Java中所有数据类型所占的字节数是与平台无关的,也没有无符号类型。绝大多数应用程序都采用double类型来表示浮点数。没有f或F后缀的浮点数默认为double类型。

检验一个值是否为NaN, 不能用 x == NaN ,因为所有非数值的值都是不相同的。 但是可用 Double.isNaN(x) 方法

CH2 OOP

对象:实例域值(状态) + 方法(行为)

优点:模块化、信息隐藏、代码重用、易维护

设计:首先从设计类开始,然后再往每个类中添加方法。分析问题时名词对应数据域,动词对应方法

原则

  • 数据封装(data encapsulation):隐藏内部状态,所有对象之间的交互通过方法来实现;同时可以完全改变存储数据的方式,而不会影响外部使用
  • 继承 :可以通过扩展一个类来创建一个新类,并自定义方法和数据域。但是只能有一个父类——单继承
    • class Cat extends Animals

接口

接口不是类,而是类的功能的描述,并不给出每个功能的具体实现。接口中所有方法默认属于public,但是在类中实现时必须显式声明为public

接口中可以定义常量,接口中的域默认为public static final。不能含有实例域或静态方法——接口没有实例,可以将接口看做没有实例域的抽象类 。但是一个类可以实现一个或多个接口——接口相比于抽象类的优势,类只能单继承。接口可以提供多继承的好处,并同时避免其复杂性和低效性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//接口声明
interface Bicycle{
void changeGear(int newValue);
void speedUp(int increment);
}
//类实现
class ACMEBicycle implements Bicycle{
int speed = 0;
int gear = 0;

public void changeGear(int newValue){
gear = newValue;
}
public void speedUp(int increment){
speed = speed + increment;
}
}

必须实现接口中的所有方法,否则会出现编译错误

Interfaces form a contract between the class and the outside world, and this contract is enforced at build time by the compiler

接口不能用new运算符,但是可以声明一个接口类型的变量,该变量必须引用实现了接口的类对象

1
2
Bicycle mybike;
mybike = new ACMEBicycle(...)

接口也可以被扩展

Java8 之后,可在接口中实现简单静态方法了,但是这些方法不能引用实例域。这样就无需为该接口编写伴随类了——面向接口编程..

考虑一个问题:当接口中新增方法时,是否所有伴随类都要对应改变——接口扩展或default方法(接口演化)

default方法出现在Java8 之后,实不实现都可以,主要是为了兼容以及按需实现

超类优先——接口与超类的冲突:如果一个类从超类继承的方法与它所实现的接口中的某个默认方法同名,那么实际上只有超类的方法有效——主要是为了与Java7兼容

接口之间的冲突:需要重写

A package is a namespace that organizes a set of related classes and interfaces

使用包的主要原因是确保类名的唯一性,建议将域名逆序作为包名。

CH3 Java基础

变量

  • 实例变量——放在heap
  • 类变量:静态变量,仅有一个,类和所有对象共享——放在方法区的静态域
  • 局部变量:方法内部定义的变量,只对该方法可见。编译器不会给局部变量设置默认值,故一定要初始化——放在stack
  • 参数

命名

变量:单个词全小写gear 多个词从第二个单词开始首字母大写 gearRatio 常量全大写,_隔开 NUM_GEARS

类名:每个单词的首字母大写

方法名:第一个单词为小写动词

Java内存区域划分

  • 栈:在函数中定义的基本类型变量对象的引用变量都在函数的栈内存中分配。栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。
  • 堆:通过new生成的对象都存放在堆中,对于堆中的对象生命周期的管理由Java虚拟机的垃圾回收机制GC进行回收和统一管理。优点是可以动态分配内存大小,缺点是由于动态分配内存导致存取速度慢。
  • 方法区:是各个线程共享的内存区域,它用于存储class二进制文件,包含了虚拟机加载的类信息、常量(常量池)、静态变量(静态域)、即时编译后的代码等数据。包括:
    • 常量池:常量池在编译期间就将一部分数据存放于该区域,包含以final修饰的基本数据类型的常量值、String字符串
    • 静态域:存放类中以static声明的静态成员变量。
    • 程序计数器:当前线程所执行的行号指示器。通过改变计数器的值来确定下一条指令,比如循环,分支,跳转,异常处理,线程恢复等都是依赖计数器来完成。

更多关于Java内存分配内容参看

关键字及保留字

不太熟悉的关键字:

关键字 含义
assert 用来进行程序调试
finally 用于处理异常情况,用来声明一个基本肯定会被执行到的语句块
native 用来声明一个方法是由与计算机相关的语言(如C/C++/FORTRAN语言)实现的
strictfp 用来声明FP_strict(单精度或双精度浮点数)表达式遵循IEEE 754算术规范
synchronized 表明一段代码需要同步执行
transient 声明不用序列化的成员域
volatile 表明两个或者多个变量必须同步地发生变化

基本数据类型

  • boolean : true and false 整型值和布尔值不能相互转换
  • byte – short – int – long(字面量加L) 没有unsigned类型,字面量0x 十六进制 0b 二进制
  • float – double 字面量加F或D或用科学计数法 要求极高精度时,用java.math.BigDecimal class
  • 所有数值类型所占据的字节数与平台无关
  • char : 16 bit Unicode ‘\u0000’ ~’\uffff’ —— 建议不要使用,用java.lang.String class抽象类型来处理

为了可读性,经常会在数值字面量中加入‘_’ 。但是要注意只能放在数字之间,不能用于

  • At the beginning or end of a number —— 数字首末
  • Adjacent to a decimal point in a floating point literal ——小数点前后
  • Prior to an F or L suffix —— F/L 之前,x/b 前后
  • In positions where a string of digits is expected —— 字符型数字

数组

内置方法

长度:.length

复制:System class 中有 public static void arraycopy(Object src, int srcPos,Object dest, int destPos, int length)

java.util.Arrays class 提供的方法

  • Searching an array for a specific value to get the index at which it is placed (the binarySearch() method).
  • Comparing two arrays to determine if they are equal or not (the equals() method).
  • Filling an array to place a specific value at each index (the fill() method).
  • Sorting an array into ascending order. This can be done either sequentially, using the sort() method, or concurrently, using
    the parallelSort() method introduced in Java SE 8

位运算符

对整数的二进制位进行处理,得到某个位的值。

& and | or ^ xor ~ not

>>> 无符号右移,高位补0 >> 高位补符号位 没有无符号左移

利用&结合使用适当的2的幂,可以用掩码技术把其他位掩掉,只保留其中的某一位 如n & 0b1000

注意: & 和 | 不采用短路方法

控制语句

在for循环中:If the variable that controls a for statement is not needed outside of the loop, it’s best to declare the variable in the initialization expression. for(int i = 1; i < n; i++)

枚举循环 for(int item : numbers)

加了标签的breakcontinue 语句可以同时跳出多层循环

CH4 类和对象

构造器

不要在构造器中定义与实例域重名的局部变量,因为会屏蔽实例域,而这些局部变量只能在构造器内部访问。当被屏蔽时可用this.来访问实例域 ,如 this.name = name this还可以用来调用同一类的另一个构造器,这样可以减少构造器公共部分的代码

Java允许重载任何方法,而不仅仅是构造器方法

仅当类没有提供任何构造器时,系统才会提供一个默认的无参构造器

调用构造器的具体步骤:—— 一般也应按以下顺序声明类,构造器通过new运算符调用,正因为构造器与类名一样,new才知道调用哪个,构造完毕返回一个引用

  1. 所有数据域被初始化为默认值(0,false,null)
  2. 按照在类声明中出现的次序,依次执行所有域初始化语句和初始化块——只要构造类的对象,这些块就会被执行。编译器会把初始化块(包括static初试化块)复制到每一个构造器中,因此可以实现复用
  3. 如果构造器第一行调用了第二个构造器,则执行第二个构造器
  4. 执行这个构造器的主体

参数传递

可以传基本数据类型、引用数据类型,但是不允许传方法——可以传对象,再调用对象的方法

基础类型参数传值,实际上在方法内部复制了一份,内部改变对外部传入的原始值没有影响。随着方法执行完毕,这份复制也随之消失。

引用类型参数也是传的值,只不过这个值是引用,即地址。复制的也是地址,指向同一个区域,可以对所指对象进行修改。

方法返回值

返回值声明为类时,可返回该类及其子类——也可以重写一个方法,使它返回子类,此为协变返回类型

​ 具体来说:协变返回类型(Covariant Return Type),指的是当一个类被继承之后,该类中方法的返回类型变成子类对应的类型,这个改变后的返回类型就叫协变返回类型。—— 在方法重写中常用

返回值声明为接口时,可返回实现了该接口的类

this关键字

this指向被构造的对象

当实例域被构造器或方法的参数屏蔽时,可在构造器和方法的内部用this关键字访问对象的实例域——还有个方法就是参数直接起不一样的名字

this关键字还可以用在构造器的第一行,用来调用该类的另一个构造器,称之为显式构造方法的调用——这样可以省略构造器的公共部分,如

1
2
3
public Rectangle(int width, int height){
this(0, 0, width, height);
}

访问修饰符

默认为package-private

类修饰符有public和默认,成员另还有private和protected

protected 能被本包所有类及其他包中该类的子类访问

static修饰符

属于类且不属于类对象的变量和函数,建议用类名直接访问

一个常用的静态常量System.out:

1
2
3
4
public class System{
...
public static final PrintStream out = ...;
}

其中用static使得不需要创建对象就可以调用,final 用来防止被修改

静态方法没有this参数,所以不能访问实例域,但是可以访问自身类中的静态域,或者通过对象引用间接访问实例域和实例方法

使用静态方法的情况:无需访问实例域;只需访问静态域

嵌套类

作用:加强封装,对只在一个地方使用的类进行合理的组织

嵌套类作为外围类的成员,因此可以被声明为private或protected

非静态嵌套类——内部类:可以访问外围类的所有成员,包括私有的。由此可见,内部类具有访问特权。在创建内部类实例之前,必须首先创建外围类的实例。

OuterClass.InnerClass innerObject = outerObject.new InnerClass();

编译器修改了所有内部类的构造器,添加一个外围类引用的参数,用来访问外围类

静态嵌套类不能访问外部类的其他成员,而且跟静态方法一样,通过外围类名进行访问。静态类可以有静态域和方法——只隐藏即可,无需访问外部

局部类:不用public或private访问说明符声明,可以对外部世界完全隐藏 。不仅可以访问外围类,还可以访问final类的局部变量。局部类访问final局部变量或外围类参数的过程,称为变量捕获?—— 编译器在检测到对局部变量的访问之后,会在局部类的构造器中中拷贝一份局部变量,为了保证数据的一致性,所以必须为final

局部类和内部类都不能定义或声明静态方法,如果有静态域,必须是final的

匿名类:声明和实例化同时进行,而且仅创建这个类的一个对象,因此不用命名。可以实现接口,也可以是对超类进行扩展。因为没有名字,所以没有构造器,只能将构造器参数传递给超类构造器—— 访问权限及内部成员限制跟局部类一样

1
2
3
4
5
6
7
8
// 实现接口,接口没有构造器,不能传参
new InterfaceType(){
methods and data
}
// 扩展超类
new SuperType(construction parameters){
inner class methods and data
}

枚举类型

通过enum关键字来定义,如

1
public enum Day { SUNDAY, MONDAY, TUESDAY, WEDNESDAY,THURSDAY, FRIDAY, SATURDAY }

枚举类型有一系列预定义的常量实例,也可以添加构造器、方法和域。比较时用== 不用equals()

所有枚举类型都是Enum类的子类,继承的方法有

  • String toString()——返回枚举常量名
  • static Enum valueOf(Class enumClass,String name) ——返回指定名字、给定类的枚举常量
  • values() ——返回一个包含全部枚举值的数组Day[] values = Day.values()
  • int ordinal() 返回枚举常量在enum声明中的位置,从0开始计数
  • int compareTo(E other) 判断次序

通常与for......each 结合用来遍历