HeadFirst-Java

摩森特沃 2021年07月13日 400次浏览

构造器与垃圾搜集器

抽象类

  • 抽象类也有构造函数,只是不能使用new来创建对象

对象

  • 对象只会被保存在堆中

实例变量

  • 实例变量存在于对象所属的堆空间上
  • 如果实例变量全都是primitive主数据类型的,则Java会依据primitive主数据类型的大小为该实例变量留下空间,int需要32位,long需要64位,依次类推,Java并不在乎变量的值,不管是32或者3200,都会占用32位
  • 如果实例变量包含引用数据类型,依然会为该变量保留空间,但不是引用的对象本身的空间,此时有以下两种情况
    • 只声明变量,没有赋值时,只会留下变量空间
    • 被赋值时,所引用的对象在堆上开辟新的空间,但此空间不包含在引用方的空间中
  • 在继承了其他类的类创建对象时,只会创建一个单一的对象(不考虑成员变量为引用类型),但会为父类的成员变量开辟空间,纳入到自己的变量空间中

局部变量

  • 无论对象是否创建,如果局部变量是个对该对象的引用,则只有变量本身会放在栈中,对象本身只会存在于堆中

构造方法

  • 唯一能调用构造函数的方法就是新建一个类
  • 在构造函数中会在代码的第一行调用父类的构造方法,一直到Object类,但是默认只会调用父类的无参构造方法
  • 可以使用this()来从某个构造方法调用同一个类的另一个构造方法,this()只能用在构造函数中,且必须是第一行语句。super()和this()不能同时使用

生命周期

  • 局部变量只会存活在声明该变量的方法中,但是局部变量只能在声明它的方法在执行中才能被使用
  • 实例变量的寿命与对象相同,如果对象活着,则实例变量也会是活着的

数字与静态变量

静态变量的初始动作

  • 通常Java虚拟机会加载某个类的时机
  • 第一次尝试要创建该类的新实例
  • 该类的静态方法或者静态变量被使用
  • 静态项目初始化的两项保证
  • 静态变量会在该类的任何对象创建之前就完成初始化
  • 静态变量会在该类的任何静态方法执行之前就初始化
  • 静态块
  • 静态块也是在加载类时会执行的程序代码,通常可用来放final变量的初始化的程序
  • final关键字
  • final的变量代表不能改变它的值
  • final的方法代表不能被重写
  • final的类代表不能被继承

异常处理

try-catch-finally

  • finally块是用来存放不管有没有异常都得执行的程序
  • 如果try或catch块有return指令,finally还是会执行,流程会跳到finally然后再回到return指令
  • 有多个catch块时,需要将处理的存在父子级关系的异常类的范围从小排到大,否则无法通过编译,因为某些异常处理块不会被用到

序列化和文件的输入输出

对象的序列化

  • 当对象被序列化时,被该对象引用的实例变量也会被序列化。且所有被引用的对象也会被序列化,这些操作都是自动进行的
  • 静态变量不会被序列化,因为所有对象都是共享同一份静态变量值
  • 如果两个对象都有引用实例变量指向相同的对象,则只会有一个对象被序列化存储,其他引用会复原成指向该对象
  • 对象序列化时,整个对象版图都必须正确地序列化,否则就只能全部序列化失败
  • 如果某实例变量不能或不应该被序列化,应该将其标记为transient(瞬时)的

对象的反序列化

  • 反序列化时会将对象放在堆中,但不会执行构造函数
  • 如果对象在继承树上有一个不可序列化的祖先类,则该不可序列化类以及在他之上的类的构造函数(就算是可序列化也一样)就会执行,一旦构造函数连锁启动之后将无法停止,即从第一个不可序列化的父类开始,全部都会重新初始状态

serialVersionUID

  • 每当对象被序列化时,该对象(以及所有在其版图上的对象)都会被加上一个类的版本识别id,这个id被称为serialVersionUID,它是根据类的结构信息计算出来的,在反序列化时,如果类有了不同的serialVersionUID,则还原操作会失败,解决的方案如下
  • 把serialVersionUID放在类中,让类即使在修改后还维持相同的ID,至于序列化的对象与类无法完全匹配,则虚拟机采用默认值填充的方式进行还原

网络与线程

Socket

  • Socket是个代表两台机器之间网络连接的对象

TCP端口

  • 一个地址可以有65536个不同的端口号可以使用,分别是0~65535
  • 通常0~1023端口是保留给特定服务使用的,自己的应用程序不应该再使用这些端口

集合与泛型

hashCode和equals

  • 如果两个对象相等,则hashCode必须相等(a.equals(b)与a.hashCode==b.hashCode等值),如果两个对象的hashCode相等,则他们不一定相等(a.hashCode==b.hashCode不一定要与a.equals(b)等值)
  • 基于上述原因,如果equals被重写,则hashCode也必须被重写
  • hashCode的默认行为是对在堆上的对象产生独特的值,如果没有重写过hashCode,则类的两个对象不可能相等
  • equals的默认行为是执行==的比较,也就是会比较两个引用是否对上堆上的同一个对象,如果equals没有被重写过,两个对象永远都不可能相同

泛型与多态

  • 数组的类型是运行期间检查的,集合的类型检查只会发生在编译期
# 基于上述原因,以下代码编译时无法通过
public void takeAnimals(ArrayList<Animal> animals) {
    anmals.add(new Cat());
}
takeAnimals(List<Dog> dogs);
# 同样基于上述原因,以下代码可通过编译
public void takeAnimals(Animal[] animals) {
    animals[0] = new Cat();
}
takeAnimals(Dogs[] dogs);

以下两行代码意义相同

public<T extends Animal> void takeThing(ArrayList<T> list);
public void takeThing(ArrayList<? extends Animal> list);
# 同样,以下代码意义也相同
public <T extends Animal> void takeThing(ArrayList<T> one, ArrayList<T> two);
public void takeThing(ArrayList<? extends Animal> one, ArrayList<? extends Animal> two);

远程部署的RMI

servlet与jsp

servlet

  • servlet是完全在http服务器上运行的java程序,主要用来处理与用户交互的网页程序
  • 必须要有支持的servlet的web服务器才能运行servlet,比如tomcat
  • 一般的servlet是继承HttpServlet并覆盖doGet和doPost方法来创建

jsp

  • jsp代表java server pages,web服务器最终会把jsp转换为servlet
  • jsp和servlet的差别在于,servlet是写出带有html输出的类,jsp刚好相反,是写出带有java程序的网页