反射相关知识单独说
5.1 类 超类 子类
概念不表
类名extends表继承,class Manager extends Employee{添加新方法和域}
子类自动继承父类所有方法和域
在子类中可以编写和父类同名方法来override覆写该方法
但是子类的方法不能直接访问父类私有域,必须借助公有接口。
当覆写需要调用同名父类方法时,记得加上super.方法(),否则会死递归。
覆写时,注意返回类型的兼容性。
super是一个特殊关键字,不是引用。
子类的构造器内可以调用超类构造器对父类域初始化,形式为super(param1,param2,....);
对象变量能够指示多种实际类型称为多态。
多态 (is-a)
子类对象可以放进父类的引用,对此引用调用覆写方法也能够被编译器识别(动态绑定),虚拟机知道实际引用的类型。但是父类变量不能用子类中新定义的方法。
子类数组的引用可以变成超类数组引用,但是超类数组的引用不能在此时赋给超类对象:
Manager[] managers=new Manager[10];
Employee[] employees=managers;
employees[0]=new Employee();
但是单独的变量或整个改变就不非法:
Manager managers=new Manager();
Employee employees=managers;
employees=new Employee();
或者
Manager[] managers=new Manager[10];
Employee[] employees=managers;
employees=new Employee[10];
顺利运行,忌讳的是扰乱数组存储空间的做法。
动态绑定
调用过程:
1)查找引用的类对象所有同名方法以及超类中的public同名方法
2)重载解析,查找应当调用的方法。 ()
3)private、static、final的方法为静态绑定。动态绑定是在运行时根据实际类型进行参数选择。
4)动态绑定中,从子类向上寻找最匹配的方法。(除非使用super参数)
虚拟机中有预先创建的方法表
动态绑定的特点是无需修改现存代码就可以对程序进行拓展,编译器选出要调用的方法。
覆写时子类方法的可见性不能低于父类
阻止继承:final 确保不会改变语义
类:final class Executive extends Manager
方法:public final String getname(){}
虚拟机的即时编译器能确认是否对方法进行内联以及编译优化
强制类型转换:类对象的引用也可以强制转换,用以使用子类对象全部功能。
Employee[] staff = new Employee[10];
staff[0]=new Manager();
//staff[0].fuck()//该句编译失败
((Manager)staff[0]).fuck();//fuck方法是Manager独有的
父类变量可以放子类对象,编译器自动通过。子类变量放父类对象时,必须进行强制类型转换,并且该对象必须实质上属于子类对象,转换才可能成功。
所以类型转换前必须测试类型是否满足
if(staff[i] instanceof Manager){
boss = (Manager) staff[i];
}
大多数情况下,自动绑定机制可以自动完成方法解析,就不用转换对象类型。只有在使用子类特有方法时候需要转换。尽量少用instanceof 和类型转换。
抽象方法:仅声明不用实现的方法。
public abstract String getDescription();
含有抽象方法的类必须定义为抽象类:
abstract class Person{
private String name;
public Person(String name){this.name=name;}
public abstract String getDescription();
//也可以有具体的方法,但不建议写在抽象类中。
}
不含抽象方法的类也可以定义为抽象方法。
抽象类的特点是不能被实例化(不可以new),但可以有抽象类变量,引用非抽象子类对象。
抽象在接口设计里用得很多
protected 关键字:对本包和所有子类可见
超类中希望被子类访问or更改的域,声明为protected。只有同一类的方法可以访问该类对象的父类数据。(Manager对象可以通过Manager方法访问Employee的域,别的不行)
5.2 Object 类
每个类自动由Object类拓展而来。Object 变量可以引用任何对象。但只是持有者,要使用需要进行类型转换。
equals()方法:不重写就是检测所比较的两个引用是不是指向同一个对象。(==语法糖)
equals应当具有的特性:
自行覆写equals:自反性、对称性、传递性、一致性
(1)检验==是否成立
if (this == other)return true;
(2)判断是否为空
if(other == null)return false;
(3)判断类型是否一样
if(getClass()!=other.getClass())return false;
(4)将other类型转换过来
Employee another=(Employee)other;
(5)开始对域进行比较(代码各异)
hashCode()方法:由对象导出一个整形值
null的hash=0 数组也可以放进hashCode方法.
toString()方法:在字符串对象和其他对象用+连接起来时候,编译器自动调用toString方法将对象变为字符串描述。
数组的toString方法没有覆写,还是Object类的方法,输出时会是乱码。要打印数组则需要
Arrays.toString(数组);//使用Arrays类写好的方法
所有自定义的类都建议实现toString方法。
5.3 泛型数组列表 ArrayList<类型>(初始容量) :一个使用类型参数的泛型类
可以自动调节数组容量
初始化:
ArrayList<Employee> staff = new ArrayList<>();
数组列表申请的空间只是有存放的潜力,在初始化之后add之前都是没有任何元素的。而数组new出来的空间都是实际空间可以任意放。
主要API
staff.add(new Employee(...)); //数组超过大小时候自动拷贝到更大空间。
staff.ensureCapacity(100); //确保(分配)100个对象空间。
staff.size();
staff.trimToSize();//内存优化到当前大小。
staff.get(int i);//取元素
staff.set(i,elem);//设定元素
staff.add(n,elem);//中间插入
staff.remove(n,elem);//中间删除
插入删除都是o(n)
for(Employee e:staff).....//foreach语法
添加完对象后,可以调用toArray方法拷贝到数组中
X[] a = new X[list.size()];
list.toArray(a);
5.4 对象包装器和自动装箱
有时,需要将int 这样的基本类型转换为对象。所有的基本类型都有一个与之对应的类。
例如:Integer 类对应基本类型 int, 通常这些类称包装器
包装器对象同string也是不可变对象
对象包装器是不可变的,即一旦构造了包装器,就不允许更改包装在其中的值。
同时,对象包装器还是final,因此不能定义它们的子类
自动装箱
自动拆箱
5.5 可变参数
构造方法时,最后一个参数类型可以加上...,这样的方法可以接受多个这样的类型,并且在方法内部形成一个数组。
public static double max(double... values){
double largest =Double.MIN_VALUE;
for(double v:values)if(v>largest)largest=v; //values 是 double[]
return largest;
}
调用为:
max(0.01,2,32.2,2412,131);
多种对象可以声明为Object... obj
5.6 枚举
public enum Size{SMALL,MEDIUM,LARGE,EXTRA_LARGE}; 定义一个4个实例的类
枚举的比较直接用==,不需要equals
所有枚举类都是ENUM类子类,有toString方法,类定义也可以加构造器和新方法。