构造器与垃圾搜集器
抽象类
- 抽象类也有构造函数,只是不能使用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程序的网页