最近在看Java核心技术卷1,温习一些java的基础知识,在工作中抑或以后可能用到或者遗忘的做一些小记录
- 一个方法是不能修改一个基本数据类型的参数值,但是对象引用就可以作为参数被修改;一个方法不能让对象参数引用一个新对象(比如交换两个对象);
- Java方法的对象引用采用的是值传递,而不是引用调用;
- 基本数据类型被初始化int(0),boolean(false);对象(null);
- equal比较两个对象是否相等是比较两个是否有相同的引用,可以认为是比较hashCode,但是这种比较其实很多时候是没有意义的,比如你去比较两个员工是否相等没有意义比较工资水平可能该更有意义,所以equal方法一般都会被重写;
关键字 可见范围 private 仅对本类可见 public 对所有类可见 protected 对本包和所有子类可见 默认 对本包可见 - 将子类的引用赋给父类编译器是允许的,但是将一个父类的引用赋给子类变量必须进行类型转换,这样才能通过类型检查;
如果隐式和显式的参数不属于同一个类,equal方法将如何处理呢?我们可能会判断如果类不匹配就返回false,但是有些人可能会使用instanceof进行检测:
if(!(otherObject instanceof Employee)) return false;
这么做不但没有解决otherObject是子类的情况,还可能招致一些麻烦。Java语言规范要求equal方法具有以下特性:- 自反性
- 对称性
- 传递性
- 一致性
- 对于任意非空引用x,x.equal(null)都应该返回false
- 散列码(hash code),是由对象导出的一个整型值,也没有规律;字符串的散列码是由内容导出的,所以两个两个"sss"字符串的hash code是一样的;Object类的默认hashCode方法导出的是对象的存储地址;StringBuffer类没有定义hashCode方法,也就是调用Object默认的方法;当然也可以重写hashCode方法,Lists(guava)就重写了这个方法,可以见上一篇的博文;如果重写equal就要重写hashCode方法;
- toString()对于调试是很有必要的
- 装箱和拆箱是编译器认可的而不是虚拟机。编译器在生成类的字节码时,插入必要的方法调用。虚拟机只是执行这些字节码;
- 比较两个枚举类型实例直接使用==即可;
- 接口中不能包含实例域或者静态方法,但是可以包含常量,他的域会被自动设为public static final
解决接口默认方法冲突:
- 超类优先。如果超类提供了一个具体方法,同名且参数类型相同的默认方法的默认方法会被忽略
- 接口冲突。如果一个接口提供了一个默认方法,另一个提供了一个同名且参数类型相同的方法,必须覆盖这个方法解决冲突
clone方法是一个Object的一个protected方法,这说明你的代码不能直接调用这个方法。
对于Object的默认colne方法克隆一个对象其实算是浅拷贝,他不会克隆对象引用的其他对象,比如Date这种可变对象,所以一般来说要重写clone方法,将这种对象在重写方法中也执行clone。但是对于不可变对象的话就不用担心这个问题。
对于每一个类需要确定:- 默认的clone方法是否满足要求;
- 是否尅在可变的子对象上调用clone来修补默认的clone方法
- 是否不该使用clone
实际上第三个选项是默认选项。如果选择第一或者第二,类必须:
- 实现Cloneable接口
- 重新定义clone方法,并指定public访问修饰符
当然克隆并不是只有这个方法,还有一种序列化反序列化的方法来做克隆操作
为什么需要内部类:
- 内部类方法可以访问该类定义所在作用域中的数据,包括私有数据
- 内部类可以对同一个包中的其他类隐藏起来
- 定义一个回调函数且不想编写大量代码使用匿名内部类很便捷
局部内部类:我们可以在一个方法中定义一个类那么这个类就是局部内部类。局部类不能用public或者private方法说明符进行声明,他的作用于被限定在声明这个局部类的块中。
优点:- 它是对外部世界完全隐藏的
- 它不仅可以访问包含他们的外部类,还可以访问局部变量,但是那些局部变量必须被声明为final
- 匿名内部类:假如只创建这个类的一个对象,就不需要命名,这就是匿名内部类
- 静态内部类:有时候使用内部类只是为了把一个类隐藏在另一个类的内部,并不需要内部类引用外部类对象,为此可以把内部类声明为static,取消产生的引用
- final关键字可以应用于局部变量。实例变量和静态变量。在所有这些情况下,他们的含义都是:在创建这个变量之后,只能为其赋值一次,此后再也不能修改它的值