继承
先贴上实验用的父类
class Employee{
private String name;
private int salary;
public Employee(String name,int salary){
this.name=name;
this.salary=salary;
}
public void setName(String name) { this.name = name; }
public void setSalary(int salary) { this.salary = salary; }
public String getName() { return name; }
public int getSalary() { return salary; }
}
定义子类
使用extends关键字,表面正在构造的新类派生于一个已经存在的类,这个已经存在的类通常称为 超类(superclass)、基类(base class) 或者父类(parent class);新类成为子类(subclass) 或者孩子类(child class)、派生类(derived class)。
尽管称之为超类或者父类,并不是因为功能更多,恰恰相反,子类比超类拥有更多的功能。子类是在超类的基础上扩展一些功能:
class Manager extends Employee {
private int bonus;
...
public void setBonus(int Bonus){} ...
}
这个代码中只是多了一个bonus字段和一些方法,超类中的方法和字段子类都会继承下来
覆盖方法
超类中的方法不一定适用于子类,所以要对超类一些功能进行改造,也就是覆盖方法
class Manager extends Employee {
private int bonus;
...
@Override
public int getSalary() {
return super.getSalary()+this.bonus;
}
}
super 关键字:
覆盖方法 getSalary 中需要调用超类的getSalary 方法,直接写成 getSalary 就会调用当前类中的getSalary方法
所以写成 super.getSalary() 即调用父类的getSalary 方法
代码中覆盖了超类的 getSalary() 方法,因为经理还有奖金。也就是说,子类就是根据需要,在超类的基础上扩展一些字段、方法,或者更改超类原有方法的功能,来实现新的功能。
所以当我们用Manager 的对象调用getSalary 方法时:
Manager boss=new Manager("BOSS",50000);
boss.setBonus(10000);
System.out.println(boss.getSalary()); //60000
注意 覆盖方法时,子类的方法不能低于超类方法的可见性。假如超类方法定义为public ,而子类覆盖时却写成了 protect 或者 private,那么就会报错。但是如果超类方法是private,那么子类可以是 private、protect或者public。也就是说子类覆盖的方法的可见性只能大于等于超类方法的可见性。
子类构造器
如果子类没有定义构造器,而且超类中有无参构造器,那么子类会自动调用超类的无参构造器。但是如果超类中没有无参构造器,而子类又没有定义构造器来调用超类的构造器,那么就会报错。
(补充一点构造器的知识:如果没有定义任何构造器,那么Java会自动定义一个无参构造器,并且设置所有字段为默认值。一旦写了一个构造器,那么这个机制就不会执行。也就是说假如定义了一个有参的构造器,那么无参的构造器必须要手动写一个才会有,Java不会自动加一个无参构造器了。)
Employee中只有一个有参构造器,那么子类继承的时候必须有构造器调用超类的构造器
class Manager extends Employee {
private int bonus;
public Manager(String name,int salary){
super(name, salary); //调用超类构造器
}
...
}
多态
非专业术语通俗的讲:
在Java中,可以将子类的对象赋给超类变量,这就称之为多态:
Employee staff;
staff = new Employee("Helen", 50000);
staff = new Manager("Helen", 50000);
这两种赋值都是合法的,第二种将子类对象赋给超类,那么此时调用的方法是子类覆盖过的方法
举个栗子:
Manager boss=new Manager("Helen",50000);
boss.setBonus(10000);
Employee staff=boss; //子类对象赋给超类
System.out.println(staff.getSalary()); //60000,这是调用子类的getSalary方法
将 boss 赋给超类Employee 变量staff 时,staff不能调用boss特有的方法,因为staff 本质上还是一个Employee的对象
boss.setBonus(100); //非法,setBonus Employee对象没有
多态是一个接口,通过不同的实例实现不同的操作(官方)
比如说上面的Employee类,子类Manager覆盖了getSalary();
现在定义一个Employee staff=new Manager() (将子类实例赋值给超类的实例,别奇怪,这就是多态的赋值方法),那么调用staff.getSalary 实际上是调用Manager覆盖后的getSalary。
阻止继承:final 类和方法
有时一个类不希望被子类继承,就可以用final 关键字修饰,那么这个类就无法产生派生类了,也就是说无法被继承:
final class A{ //A类无法被继承
private int a;
...
public void setA(){}
...
}
类中的方法也可以用final 修饰,如果这样做,子类就无法覆盖这个方法(final 类中的所有方法自动成为final 方法,但是其中的字段不会变成 final)
泛型数组列表
很多语言中必须在编译时确定数组的大小,这样很可能导致空间浪费或者空间不够,所以就有了ArrayList 类
声明数组列表
-
ArrayList
创建一个数组列表(E为指定的泛型类型,如果不懂则要先了解一下泛型)
-
ArrayList(int initialCapacity)
创建一个数组列表,初始化大小为initialCapacity
构造一个保存Employee对象的数组列表:
ArrayList<Employee> staff=new ArrayList<>(10);
添加元素和访问元素
- boolean add(E obj) 添加一个元素
- E set(int index,E obj) 用obj替换第index个元素,返回之前的内容
- E get(int index) 访问第index个元素
staff.add(new Employee("HACKER",50000));
staff.add(new Employee("Helen",50000));
//在末尾添加两个元素
Employee temp=new Employee("HH",50);
staff.set(1,temp);
//将索引为 1 的元素替换为 temp,相当于 staff[1]=temp
System.out.println(staff.get(1).getName() + " " + staff.get(1).getSalary());
//获得索引为 1 的元素
删除元素、获取元素个数
-
int size()
获取元素个数
-
E remove(int index)
删除第index个元素,并将后面的元素往前移,返回删除的元素
System.out.println(staff.size());staff.remove(1);System.out.println(staff.size());
确定大小
-
void ensureCapacity(int size)
将分配size个空间,使用这些空间时不会花费重新分配的时间
-
void trimToSize()
将容量削减到当前大小。当确定了容量不会改变后,使用这个方法将多余的空间回收掉
这两个都是为了优化用的,第一个节省时间,第二个节省空间
参数数量可变的方法
对于一些方法,有时需要的参数数量可能不一样,例如:
public int add(int a,int b){ return a+b;}
这个方法会返回两个数之和,可如果我需要三个数、四个数或者更多数之和怎么办
所以可变参数的作用就体现在这里
public int add(int... num){ int sum=0; for(int i:num){ sum+=i; } return sum;}
参数 num 是一个数组,可以接收任意多个参数。这里的类型可以是基本数据类型,也可以是类
在调用的时候可以写成 add(1) 也可以写成 add(1,2,3,4,5),你想写多少个就写多少个
注意 :可变参数在参数列表的末尾,也就是说可变参数前面可以有参数,但后面不能有
public int add(String name,int... num){} //OKpublic int add(int... num,String name){} //ERROR