1. 类、超类和子类
继承:子类继承父类的全部属性与方法,并根据需求进行扩展。关键字 extends 表示继承。
父类:已存在的类
子类:由父类派生的类,与父类是继承"is a"关系。
通用的方法放在超类中, 而将具有特殊用途的方法放在子类中
子类中可以增加域、 增加方法或覆盖超类的方法,绝对不能删除继承的任何域和方法
单根继承原则:每个类都只能继承一个类,可以实现多个接口
public class Manager extends Employee
{
添加方法和域
}
public class Manager extends Employee
{
添加方法和域
}
本章教学使用的类:
public class Employee //父类
{
private String name;
private double salary;
private LocalDate hireDay;
public Employee(String name, double salary, int year, int month, int day)
{
this.name = name;
this.salary = salary;
hireDay = LocalDate,of(year, month, day);
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public LocalDate getHireDay() {
return hireDay;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100; salary += raise;
}
}
public class Manager extends Employee //子类
{
private double bonus;
public Manager(String name, double salary, int year, int month, int day) //子类构造器
{
super(nanie, salary, year, month, day);
bonus = 0;
}
public double getSalary()//覆写
{
double baseSalary = super.getSalary(); //调用父类private域成员数据
return baseSalary + bonus;
}
public void setBonus(double b)//增加的方法 {
bonus = b;
}
}
public class Employee //父类
{
private String name;
private double salary;
private LocalDate hireDay;
public Employee(String name, double salary, int year, int month, int day)
{
this.name = name;
this.salary = salary;
hireDay = LocalDate,of(year, month, day);
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public LocalDate getHireDay() {
return hireDay;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100; salary += raise;
}
}
public class Manager extends Employee //子类
{
private double bonus;
public Manager(String name, double salary, int year, int month, int day) //子类构造器
{
super(nanie, salary, year, month, day);
bonus = 0;
}
public double getSalary()//覆写
{
double baseSalary = super.getSalary(); //调用父类private域成员数据
return baseSalary + bonus;
}
public void setBonus(double b)//增加的方法 {
bonus = b;
}
}
2. 覆盖方法(override)
:子类与父类有相同的方法(名称形参相同)
返回类型不是签名的一部分可以不同,但要保证返回类型的兼容性
子类方法不能低于超类方法的可见性。如果父类方法是 public, 子类方法一定要声明为 public
子类虽然继承父类成员,但不能直接访问父类的private域,要调用父类接口
public double getSalary()
{
return salary + bonus; // won't work
}
public double getSalary()
{
double baseSalary = getSalaryO;// still won't work
return baseSalary + bonus;
}
public double getSalary()//√
{
double baseSalary = super.getSalary();
return baseSalary + bonus;
}
public double getSalary()
{
return salary + bonus; // won't work
}
public double getSalary()
{
double baseSalary = getSalaryO;// still won't work
return baseSalary + bonus;
}
public double getSalary()//√
{
double baseSalary = super.getSalary();
return baseSalary + bonus;
}
3. 子类构造器:
先构造父类(super),再初始化子类成员。
super 调用构造器的语句必须是子类构造器的第一条语句,没有的话会默认添加
public Manager(String name, double salary, int year, int month, int day)
{
super(name, salary, year, month, day);
bonus = 0;
}
public Manager(String name, double salary, int year, int month, int day)
{
super(name, salary, year, month, day);
bonus = 0;
}
super 关键字的作用:
调用父类的方法。
调用超类的构造器。
4. 多态
:一个类对象变量可以指示多种实际类的现象被称为多态
多态的作用:以统一的接口来操控某一类中不同对象的动态行为
一般是父类变量可以指示子类对象,但子类变量不能指示父类对象,除非进行向下类型转换
一般与覆写一起使用
4.1 动态绑定
:虚拟机知道实际引用的对象类型,在运行时能够自动地选择调用哪个方法的现象。
Employee[] staff = new Employee[3];
staff[0] = boss; //多态
staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1 ); staff[2] = new Employee("Tony Tester", 40000, 1990, 3, 15);
for (Employee e : staff)
System.out.println(e.getName() + " " + e.getSalary());//虚拟机知道调用Manager 还是 Employee的 getSalary()
//但编译器将 staff[0]看成 Employee对象
boss.setBonus(5000); // OK
staff[0].setBonus(5000); // Error
Employee[] staff = new Employee[3];
staff[0] = boss; //多态
staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1 ); staff[2] = new Employee("Tony Tester", 40000, 1990, 3, 15);
for (Employee e : staff)
System.out.println(e.getName() + " " + e.getSalary());//虚拟机知道调用Manager 还是 Employee的 getSalary()
//但编译器将 staff[0]看成 Employee对象
boss.setBonus(5000); // OK
staff[0].setBonus(5000); // Error
4.2 强制类型转换
向上转换:子类向父类转换(直接转换)
向下转换:父类向子类转换(instanceof)
只能在继承层次内进行类型转换
在将父类转换成子类之前,应该使用 instanceof 进行检查
Manager boss = (Manager)staff[0]; //向上
if (staff[1] instanceof Manager)//向下 {
boss = (Manager) staff[1];
}
Manager boss = (Manager)staff[0]; //向上
if (staff[1] instanceof Manager)//向下 {
boss = (Manager) staff[1];
}
5. 抽象类
:不能被实列化的类,能提高成员变量和不被实现的方法。
abstract关键字:定义抽象类和抽象方法。
子类可以继承于抽象类,但是一定要实现父类们所有
abstract的方法。如果不能完全实现,那么子类也必须被定
义为抽象类。
抽象类的组成:
(optional)成员变量,个数不限
(optional)具体方法,方法有实现,个数不限
(optional)抽象方法,加abstract关键字,个数不限
抽象类可以包含具体数据和具体方法,但最好不要
接口一定是抽象方法,即使不加abstract
public abstract class Person {
String s;
public abstract String getDescription();
}
public abstract class Person {
String s;
public abstract String getDescription();
}
6. 接口
:是一种需求描述,内部包含抽象方法,接口不是类(interface)
public interface Comparable //接口定义
{
int compareTo(Object other);
}
public int compareTo(Object other Object) //接口实现
{
Employee other = (Employee) otherObject;
return Double.compare(salary, other.salary);
}
public interface Comparable //接口定义
{
int compareTo(Object other);
}
public int compareTo(Object other Object) //接口实现
{
Employee other = (Employee) otherObject;
return Double.compare(salary, other.salary);
}
6.1 接口的域和方法:
接口中的所有方法自动地属于 public abstract。 因此,在接口中声明方法时,不必提供关键字 public abstract
实现接口时,必须把方法声明为 public,并实现全部 abstract方法,如果没有全部实现,那么只能成为一个抽象类。
接口绝不能含有实例域,可以将接口看成 是没有实例域的抽象类,但是这两个概念还是有一定区别的
接口中不能包含实例域或静态方法,但却可以包含常量。接口中的域将被自动设为常量 public static final。
6.2 接口的特性:
接口也可以被扩展。
public interface Moveable
{
void move(double x, double y);
}
public interface Powered extends Moveable
{
double milesPerCallon();
}
public interface Moveable
{
void move(double x, double y);
}
public interface Powered extends Moveable
{
double milesPerCallon();
}
每个类只能够拥有一个超类, 但却可以实现多个接口,使用逗号将实现的各个接口分隔开。
class Employee implements Cloneable, Comparable
class Employee implements Cloneable, Comparable
接口不能直接实例化
public interface TestInterface{}
TestInterface testInterface = new TestInterface();//这种肯定是不允许的
public interface TestInterface{}
TestInterface testInterface = new TestInterface();//这种肯定是不允许的
接口可以声明变量,接受实现接口子类的赋值
– 此时该变量就是子类
– 与父类的多态不同
– 子类创建对象赋值给接口后,接口再赋值给子类需要强制转换
public interface Anim{}
public class Cat implements Anim {}
public class Dog implements Anim {}
Anim anim= new Cat();//这种是可以的,声明变量被绑定在一个以此接口实现的对象
Anim anim2= new Dog();
Cat newcat = (Cat) anim
public interface Anim{}
public class Cat implements Anim {}
public class Dog implements Anim {}
Anim anim= new Cat();//这种是可以的,声明变量被绑定在一个以此接口实现的对象
Anim anim2= new Dog();
Cat newcat = (Cat) anim
在 Java SE 8中,允许在接口中增加静态方法。理论上讲,没有任何理由认为这是不合法 的。只是这有违于将接口作为抽象规范的初衷。
6.3 接口的默认方法
:可以为接口方法提供一个默认实现,使用 default 修饰符标记。(直接进行调用或覆写)
可以把所有方法声明为默认方法, 这些默认方法什么也不做。只需要覆写需要的方法
public interface MouseListener
{
default void mousedieked(MouseEvent event) {}
default void mousePressed(MouseEvent event) {}
default void mouseReleased(MouseEvent event) {}
default void mouseEntered(MouseEvent event) {}
default void mouseExited(MouseEvent event) {}
}
public interface MouseListener
{
default void mousedieked(MouseEvent event) {}
default void mousePressed(MouseEvent event) {}
default void mouseReleased(MouseEvent event) {}
default void mouseEntered(MouseEvent event) {}
default void mouseExited(MouseEvent event) {}
}
默认方法冲突原则:
超类优先,如果超类提供了一个具体方法,同名而且有相同参数类型的默认方法会被忽略
接口冲突,如果一个超接口提供了一个默认方法,另一个接口提供了一个同名而且 参数类型(不论是否是默认参数)相同的方法,必须覆盖这个方法来解决冲突
6.4 接口示例
public interface ActionListener //监听事件
{
void actionPerfonned(ActionEvent event);
}
class TimePrinter implements ActionListener //接口实现
{
public void actionPerformed(ActionEvent event)
{
System.out.println("At the tone, the time is " + new Date());
Toolkit.getDefaultToolkit().beep();
}
}
ActionListener listener = new TimePrinter(); //类似于父类变量接受子类型
Timer t = new Timer(10000, listener);
t.start()
public interface ActionListener //监听事件
{
void actionPerfonned(ActionEvent event);
}
class TimePrinter implements ActionListener //接口实现
{
public void actionPerformed(ActionEvent event)
{
System.out.println("At the tone, the time is " + new Date());
Toolkit.getDefaultToolkit().beep();
}
}
ActionListener listener = new TimePrinter(); //类似于父类变量接受子类型
Timer t = new Timer(10000, listener);
t.start()
7. Object: 所有类的超类
:Object 类是 Java 中所有类的始祖, 在 Java 中每个类都是由它扩展而来的,构建出一个类型继承树
在 Java 中,只有基本类型 不是对象, 例如,数值、 字符和布尔类型的值都不是对象。
Object类里面默认就有clone, equals, finalize, getClass,
hashCode, toString等方法
单根继承原则:每个类都只能继承一个类,可以实现多个接口
7.1 equals() 方法
:检测一个对象是否等于另外一个对象。
检验引用是否相等
是否为同一类
内容域是否相等
public class Employee{
public boolean equals(Object otherObject)
{
// a quick test to see if the objects are identical
if (this == otherObject) return true;//检验引用
// must return false if the explicit parameter is null
if (otherObject == null) return false;
// if the classes don't match, they can't be equal
if (getClassO != otherObject.getClass()) return false;//检验是否同类
// now we know otherObject is a non-null Employee Employee
other = (Employee) otherObject;
// test whether the fields have identical values
return name.equals(other.name) && salary = other.salary && hi reDay.equals(other.hireDay);//检验内容域
}
}
public class Employee{
public boolean equals(Object otherObject)
{
// a quick test to see if the objects are identical
if (this == otherObject) return true;//检验引用
// must return false if the explicit parameter is null
if (otherObject == null) return false;
// if the classes don't match, they can't be equal
if (getClassO != otherObject.getClass()) return false;//检验是否同类
// now we know otherObject is a non-null Employee Employee
other = (Employee) otherObject;
// test whether the fields have identical values
return name.equals(other.name) && salary = other.salary && hi reDay.equals(other.hireDay);//检验内容域
}
}
getClass() 将返回一个对象所属的类
子类的equals方法:首先调用超类的 equals。如果检测失败,对象就不可能相等。如果父类中的域都相等,就需要比较子类中的实例域。
public class Manager extends Employee{
public boolean equals(Object otherObject)
{
if (!super.equals(otherObject)) return false;
// super.equals checked that this and otherObject belong to the same class
Manager other = (Manager) otherObject;
return bonus == other.bonus;
}
}
public class Manager extends Employee{
public boolean equals(Object otherObject)
{
if (!super.equals(otherObject)) return false;
// super.equals checked that this and otherObject belong to the same class
Manager other = (Manager) otherObject;
return bonus == other.bonus;
}
}
equals 方法具有下面的特性:
自反性:对于任何非空引用 x, x.equals(x)应该返回 true
对称性: 对于任何引用 x 和 y,当且仅当 y.equals(x) 返回 true, x.equals(y) 也应该返回 true
传递性:对于任何引用 x、y 和 z, 如果 x.equals(y) 返回 true, y.equals(z)返回 true, x.equals(z) 也应该返回 true
一致性: 如果 x 和 y引用的对象没有发生变化,反复调用 x.equals(y) 应该返回同样的结果。
如果子类能够拥有自己的相等概念,则对称性需求将强制采用 getClass ()进行检测
如果由超类决定相等的概念,那么就可以使用 imtanceof()进行检测
7.2 hashCode() 方法
:散列码( hash code) 是由对象导出的一个整型值,如果x与y对象相同则 x.hashCode( ) == y.hashCode( )
public class Employee
{
public int hashCode() {
return 7 * name.hashCode() + 11* new Double(salary).hashCode0 + 13 * hireDay.hashCode();
}
}
public class Employee
{
public int hashCode() {
return 7 * name.hashCode() + 11* new Double(salary).hashCode0 + 13 * hireDay.hashCode();
}
}
Equals 与 hashCode 的定义必须一致:如果 x.equals(y) 返回 true, 那么 x.hashCode( ) 就必 须与 y.hashCode( ) 具有相同的值。
hashCode相等,equals不一定相等
hashCode相同,再判断equals,equals同则相同,不同则不同
7.3 toString() 方法
:它用于返回表示对象值的字符串,或自己所需信息的字符串
public String toString()
{
return getClass().getName() + "[name=" + name +",salary: " + salary + ",hireDay=" + hireDay + "]";
}
public String toString()
{
return getClass().getName() + "[name=" + name +",salary: " + salary + ",hireDay=" + hireDay + "]";
}
8. 对象包装器与自动装箱
包装器:要将 int 这样的基本类型转换为对象
:Integer、Long、Float、Double、Short、Byte、Character、Void 和 Boolean
ArrayList<Integer> list = new ArrayList<>();
list.add(3);//将自动地变换成 list.add(Integer.value0f(3));
//这种变换被称为自动装箱(autoboxing)。
ArrayList<Integer> list = new ArrayList<>();
list.add(3);//将自动地变换成 list.add(Integer.value0f(3));
//这种变换被称为自动装箱(autoboxing)。
public class BoxClassTest {
public static void main(String[] args)
{
int i1 = 10;
Integer i2 = 10; // 自动装箱
System.out.println(i1 == i2); //true
// 自动拆箱 基本类型和包装类进行比较,包装类自动拆箱
Integer i3 = new Integer(10);
System.out.println(i1 == i3); //true
// 自动拆箱 基本类型和包装类进行比较,包装类自动拆箱
System.out.println(i2 == i3); //false
// 两个对象比较,比较其地址。
// i2是常量,放在栈内存常量池中,i3是new出对象,放在堆内存中
Integer i4 = new Integer(5);
Integer i5 = new Integer(5);
System.out.println(i1 == (i4+i5)); //true
System.out.println(i2 == (i4+i5)); //true
System.out.println(i3 == (i4+i5)); //true
// i4+i5 操作将会使得i4,i5自动拆箱为基本类型并运算得到10.
// 基础类型10和对象比较, 将会使对象自动拆箱,做基本类型比较
Integer i6 = i4 + i5; // +操作使得i4,i5自动拆箱,得到10,因此i6 == i2.
System.out.println(i1 == i6); //true
System.out.println(i2 == i6); //true
System.out.println(i3 == i6); //false
}
}