1 在多态中访问子类特有成员
将父类的引用 转换成 子类的引用类型
1.1 多态的转型
基本类型的类型转换:
- 自动类型转换:小的数据类型可以自定转换为大的数据类型
- 强制类型转换:可以把打的数据类型强制转换为小得数据类型,与可能造成数据的精度损失。
java中对象的强制类型转换称为造型。
- 从子类到父类类型的转换可以自动进行。
- 从父类到子类的类型转换必须通过造型(强制转换)实现
- 无继承关系的引用类型间的转换是非法
1.2 instanceof
作用就是检验某一个引用是否是另一个类的实例对象。 返回值是boolean类型
if(ac instanceof Cat){//返回true则表示是该类的示例对象
}
-
子类到父类的类型转换 称为向上转型
-
父类到子类的类型转换 称为向下转型,需要强制转换
1.3 继承成员变量和继承方法的区别
若子类重写父类的方法,就意味着子类中定义的方法彻底覆盖了父类中的同名方法,系统将不会将父类中的方法转移到子类。
对于实例变量则不存在这样的现象,即使子类定义了与父类完全相同的实例变量, 这个实例变量依然不可能覆盖父类中的实例变量。
案例:计算图形面积
定义三个类,父类GeometricObject代表几何形状,子类Circle代表圆形, MyRectangle代表矩形。
定义一个测试类GeometricTest,
编写equalsArea方法测试两个对象的面积是否相等(注意方法的参数类型,利用动态绑定技术),编写displayGeometricObject方法显示对象的面积(注意方法的参数类型,利用动态绑定技术)。
2 . 面向对象的三大特征在JDK中的体现
- java.lang.Object
public class Object
Object 类是所有的java类的根父类。他是java中所有类的直接或间接的超类(基类)
如果在类的声明中,没有使用extends 关键字来指明其父类,则默认的父类就是Object
public class Animal extends Object {}
2.1 Object类的主要的结构
构造方法:
Object()
成员方法:
equals(Object obj)
指示一些其他对象是否等于此。finalize()
当垃圾收集确定不再有对该对象的引用时,垃圾收集器在对象上调用该对象。hashCode()
返回对象的哈希码值。toString()
返回对象的字符串表示形式。
2.1.1.finalize
通知垃圾收集器回收当前对象,释放空间。但是,垃圾回收器并不一定会立即执行对象的收集和空间的释放。
2.1.2 equals
Object中equals的实现
public boolean equals(Object obj) {
return (this == obj);
}
String类中的equals
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String aString = (String)anObject;
if (coder() == aString.coder()) {
return isLatin1() ? StringLatin1.equals(value, aString.value)
: StringUTF16.equals(value, aString.value);
}
}
return false;
}
当一个类 在没有重写equals方法的时候 ,调用equals方法比较 本质上就是==比较
所有类都继承了Object ,也就获得了equals方法。如果要比较对象的属性是否形同,判定对象是否形同,则必须重写equals方法。
@Override
public boolean equals(Object obj) {
Student stu= null;
if(obj instanceof Student){
stu = (Student) obj;
}
if(this.getName().equals(stu.getName())&&this.getAge() == stu.getAge()){
return true;
}
return false;
}
重写之后,equals方法的比较规则,方法的实现,可以自行实现。
2.1.3.重写规则:
1 对称性 如果x.equals(y) 返回true 那么 y.equals(x) 也应该返回true
2 自反 性 x.equals(x) 必须返回的也是true
3 传递性 如果 x.equals(y) 是true y.equals(z) 是true 那么 x.equals(z)应该也返回的是true
4 一致性 如果x.equals(y) 是true 只要x和y的内容不变,无论重复x.equals(y)多少次 结果应该都是true
在任何情况下,x.equals(null) 返回的都是false 。
2.1.4 toString
System.out.println(stu1.toString());//cn.lanqiao.oop.Student@49e4cb85
System.out.println(stu2.toString());// cn.lanqiao.oop.Student@2133c8f8
toString方法返回的是String类型,类名+"@"+引用地址
在定义一个类的时候 我们也需要重写toString方法 。来根据我们需要 ,以字符串的形式,输出对象的相关内容
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
在idea中,因为equals方法和toString方法的重写,使用频率较高,所以提供了自动重写的方式
2.1.5 hashCode
返回对象的哈希码值 在一定的程度上 可以将hash值理解为对象的内存空间的地址值。
-
如果两个对象的equals返回true 则他们的hashCode方法 返回的hash码值肯定也是相同的
-
如果两个对象的hash值相同,那么他们的equals方法不一定为true
在一般情况下,当我们创建一个类的时候,我们都需要重写equals和toString();当重写equals的同时,也必须重写hashCode
3 抽象类和抽象方法
3.1 抽象类的定义
用abstract 修饰的类 就是抽象类
用abstract 修饰方法 就是抽象方法
3.2 抽象类的特点
- 抽象类不能示例化
- 抽象类是用来被继承的
- 可以通过多态来访问成员
3.3 抽象方法的特点
- 使用abstract 关键字修饰
- 抽象方法不能有方法体
- 抽象方法是用来被重写的
3.4 抽象类和抽象方法的关系
- 抽象类可以没有抽象方法
- 包含抽象方法的类一定是抽象类
3.5 抽象类的子类
抽象类的子类 要么是抽象类,要么就重写父类中的抽象方法
3.6 抽象类的成员特点
- 成员变量:变量、常量
- 构造方法:有 意义:方便子类使用父类的成员数据
- 成员方法:
抽象方法:限定子类中必须完成的一些特定的功能
非抽象方法:提高了代码的复用性
- 编写一个Employee类,声明为抽象类,包含如下三个属性: name, id, salary。提供必要的构造器和抽象方法: work()。
- 对于Manager类来说,他既是员工,还具有奖金(bonus)的属性。请使用继承的思想,设计CommonEmployee类和Manager类,要求类中提供必要的方法进行属性访问。
public abstract class Employee {//抽象类
int id;
String name;
double salary;
public Employee() {
}
public Employee(int id, String name, double salary) {
this.id = id;
this.name = name;
this.salary = salary;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public abstract void work();
}
public class CommonEmployee extends Employee {//子类1
public void work(){
System.out.println("普通员工……");
}
}
public class Manager extends Employee{//子类2
double bonus;
public void work(){
System.out.println("经理:管理普通员工……");
}
}
4 模板方法设计模式
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的 通用模板。子类在抽象类的基础上进行扩展改造,但是子类在总体上保留抽象类的行为方式
解决问题:
当功能内部一部分实现是去认定的,一部分是不确定的,可以把不确定的部分暴露出去 让子类去实现
模板模式:
在软件开发中 对于一个算法而言,整体步骤是固定的,通用,让这些步骤我们就可以定义父类中,但是其中某一部分是易变的 此时可以将易变的部分抽出来定义成抽像,让子类重写实现
//计算代码的执行时间
public abstract class Template {
public void getTime(){
//获取开始时间
long begin = System.currentTimeMillis();
// 让我们要测试代码开始执行
code();
//获取代码执行结束时的时间
long end = System.currentTimeMillis();
System.out.println("执行代码共耗时:" + (end - begin) + "毫秒");
}
public abstract void code();
}
public class SubTemplate extends Template{
@Override
public void code() {
for(int i = 0 ; i < 10000;i++){
System.out.println(i);
}
}
}
public class TemplateTest {
public static void main(String[] args) {
Template temp = new SubTemplate();
temp.getTime();
}
}
5 接口
接口是一种公共的规范标准,只要符合规范标准,就可以通用
Java中 的接口更多的体现在行为的抽象
Java中 接口的本质是契约 规范
5.1 接口的定义
interface 和 class 处于同一级、
public interface 接口名{}
5.2 接口的成员特点
- 接口中的成员方法:只能是抽象方法,默认修饰符是public abstract
- 接口中没有构造方法
- 接口中的变量默认是常量 static final
5.3 接口的特点
- 接口不能被实例化
- 接口是 用来被实现的
- 实现接口 就要实现接口中所有的抽象方法。要么实现类是抽象类,要么实现接口的方法都是抽象方法
public interface Dao {
int a = 10;
void add();
void remove();
void insert();
}
public class DaoImpl implements Dao{
@Override
public void add() {
}
@Override
public void remove() {
}
@Override
public void insert() {
}
}
public class DaoTest {
public static void main(String[] args) {
Dao dao = new DaoImpl();// 接口的多态
int aa = Dao.a;
System.out.println(aa);
}
}
多态的体现: 具体类的多态 抽象类的多态 接口的多态
多态的前提:继承或实现
要有方法的重写:父类(父接口)的引用指向子类(子接口)的对象
5.4 接口和类的关系
-
类和类的关系
继承关系 只能是单继承,可以是多层继承 -
接口和类的关系
实现关系,可以是单实现,也可以多实现。在实现的同时继承一个类 -
接口间的关系
接口可以继承接口 接口的继承也是单继承 也可以多重,多层继承
5.6 抽象类和接口的区别
区别点 | 抽象类 | 接口 |
---|---|---|
定义组成 | 包含抽象方法的类 具体方法 变量 构造方法 | 抽象方法和常量 |
使用 | 子类继承抽象类 | 实现类实现接口 |
关系 | 抽象类实现接口 | 接口不能继承抽象类,可以继承多个接口 |
对象 | 通过抽象类的多态性产生实例对象 | 无 |
局限 | 只能单继承 | 没有 |
6参数传递
6.1使用类名作为形参和返回
- 类名作为方法的形参
方法的形参是类类型 实参就是对应类型的对像
– 参数传递时 传递的是对象的地址值 - 类名作为方法的返回值
方法的返回值是类类型,真正返回的是该类的对象。实际返回的是对象的地址值
– 当在参数传递的时候,如果传递的是类类型,本质都是传递的对象的引用地址值
6.2 抽象类作为形参和返回值
1. 抽象类作为形参和返回值
方法的形参是抽象类类型 实参子类对象 实际传递的是子类对象的地址值
方法的返回值 返回的是子类对象 实际也是子类对象的地址值
2. 接口作为形参和返回值
方法的形参是接口类型 实参实现对象 实际传递的是实现对象的地址值
方法的返回值 返回的是实现对象 实际也是实现对象的地址值
6.3 综合案例
运动员和教练:
需求:
现在有乒乓球运动员和篮球运动员,乒乓球教练和篮球教练。为了参见东京奥运会,其他国家的运动员进行交流,乒乓球运动员需要学习英语。篮球运动员需要学习日语在这个案例中,那些是具体的类 那些是抽象类 那些是接口
分析:
乒乓球运动员
篮球运动员
教练
思路:
1 定义学习外语的接口 成员方法:学外语
2 定义抽象人类 成员变量:姓名 年龄 构造方法 无参 带参 成员方 getter/setter 吃饭 睡觉
3 定义教练类:继承人类 构造方法 抽象方法 教
4 定义运动员类 继承人类,构造方法 抽象方法 play
5 定义具体的类:
乒乓球运动员 篮球运动员 都需继承运动员 并实现学习外语的接口 实现抽象方法
乒乓球教练 篮球教练 都需要继承教练抽象类
public abstract class Human {
int age;
String name;
public Human() {
}
public Human(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public interface Learn {//接口
public void learn();
}
public abstract class Student extends Human{//运动员
public Student() {
}
public Student(int age, String name) {
super(age, name);
}
public abstract void eat();
public abstract void play();
}
public class StuBasket extends Student{//篮球运动员类
public StuBasket() {
}
public StuBasket(int age, String name) {
super(age, name);
}
public void play(){
System.out.println("篮球运动员……");
}
public void learn(){
System.out.println("篮球运动员学日语");
}
}
public class StuPinpang extends Student{//乒乓球运动员
public StuPinpang() {
}
public StuPinpang(int age, String name) {
super(age, name);
}
public void play(){
System.out.println("乒乓球运动员……");
}
}
public void learn(){
System.out.println("乒乓球运动员学英语");
}
public abstract class Teacher extends Human{//教练类
public Teacher() {
}
public Teacher(int age, String name) {
super(age, name);
}
public abstract void teach();
}
public class TeaBasket extends Teacher{//篮球教练
public TeaBasket() {
}
public TeaBasket(int age, String name) {
super(age, name);
}
public void teach(){
System.out.println("篮球教练……");
}
}
public class TeaPinpang extends Teacher{//乒乓球教练
public TeaPinpang() {
}
public TeaPinpang(int age, String name) {
super(age, name);
}
public void teach(){
System.out.println("乒乓球教练……");
}
}
public class Test {
public static void main(String[] args) {
StuBasket stu1 = new StuBasket();
TeaBasket tea1 = new TeaBasket();
StuBasket stu2 = new StuBasket();
TeaBasket tea2 = new TeaBasket();
stu1.play();
stu1.learn();
tea1.teach();
stu2.play();
stu2.learn();
tea.teach();
}
}
7.内部类
就是定义在类的内部的类
格式:
class 外部类{
修饰符 class 内部类{
}
}
内部类的访问特点:
内部类可以直接访问外部类的成员 包括私有成员
外部类要访问内部类的成员 必须创建对象
7.1 成员内部类
根据内部类定义的位置:定义在类中,和方法是同一级别的
在外部类内创建成员内部类的对象格式:
外部类.内部类 内部类对象名 = new 外部类().new 内部类();
Outer.Inner inner = new Outer().new Inner();
成员内部类的使用:
将一个类设计成内部类,大多是因为不想外界访问,所以内部类的定义应该先私有化,私有化之后再提供一个可以让外界调用的方法,方法内部常见内部类对象并调用
7.2 方法内部类
局部内部类:定义在方法中的类
访问范围仅限于所在的方法内,方法之外无法访问。在内部访问时需创建内部类的对象
7.3 匿名内部类
匿名内部类的前提:
存在一个类或接口,这里的类可以是抽象类也可以是具体类
格式:
new 类名(){
重写的方法
}
new 接口名(){
实现方法
}