public class Animal { String name; int age; public void eat(){ System.out.println("动物进食....."); } }
public class Cat extends Animal{ String name; int age; @Override public void eat() { System.out.println("猫吃鱼...."); } public void play(){ System.out.println("猫抓老师....."); } }
public class Dog extends Animal { String name; int age; @Override public void eat() { System.out.println("狗吃骨头...."); } public void work(){ System.out.println("狗看家...."); } }
public class AnimalTest { public static void main(String[] args) { Animal ac = new Cat(); ac.eat();//只能访问到子类对于父类重写的方法 Animal ad = new Dog(); ad.eat(); Cat c = new Cat(); c.eat(); c.play(); Dog d = new Dog(); d.eat(); d.work(); } }
将父类的引用转换为子类的引用对象,就能够访问到子类特有的方法了。
基本类型的类型转换:
自动类型转换:小的数据类型可以自定转换为大的数据类型
强制类型转换:可以把大的数据类型强制转换为小得数据类型,与可能造成数据的精度损失。
java中对象的强制类型转换称为造型。
从子类到父类类型的转换可以自动进行
从父类到子类的类型转换必须通过造型(强制转换)实现
无继承关系的引用类型间的转换是非法
Cat cat = (Cat) ac; cat.play(); Dog dog = (Dog)ad; dog.work();
作用就是检验某一个引用是否是另一个类的实例对象。 返回值是boolean类型
if(ac instanceof Cat){ Cat cat = (Cat) ac; cat.play(); } Dog dog = (Dog)ad; dog.work(); System.out.println("--------------------------"); // 以下的转换是失败的 if(ac instanceof Dog){//返回true 则表示是该类的实例对象 Dog cd = (Dog) ac; cd.work(); }else{ System.out.println("类型不匹配"); } if(ad instanceof Cat){ Cat dc = (Cat) ad; dc.play(); }else{ System.out.println("类型不匹配"); }
将有子类到父类的类型转换 称为向上转型
将有父类到子类的类型转换 称为向下转型,需要强制转换
package cn.lanqiao.oop; public class FieldMethodTest { public static void main(String[] args) { Sub sub = new Sub(); System.out.println(sub.count);//20 sub.display();//20 Base b = sub; System.out.println(b == sub);//true System.out.println(b.count);//10 b.display();//20 } } class Base{ int count =10; public void display(){ System.out.println(count); } } class Sub extends Base{ int count = 20; public void display(){ System.out.println(count); } }
若子类重写父类的方法,就意味着子类中定义的方法彻底覆盖了父类中的同名方法,系统将不被坑不父类中的方法转移到子类。
对于实例变量则不存在这样的现象,即使子类定义了与父类完全相同的实例变量 这个实例变量依然不可能覆盖父类中的实例变量。
案例:计算图形面积
定义三个类,父类GeometricObject代表几何形状,子类Circle代表圆形, MyRectangle代表矩形。
定义一个测试类GeometricTest, 编写equalsArea方法测试两个对象的面积是否相等(注意方法的参数类型,利用动态绑定技术),编写displayGeometricObject方法显示对象的面积(注意方法的参数类型,利用动态绑定技术)。
java.lang.Object
public class Object
Object 类是所有的java类的根父类。他是java中所有类的直接或间接的超类(基类)
如果在类的声明中,没有使用extends 关键字来指明其父类,则默认的父类就是Object
public class Animal extends Object {
构造方法:
Object()
成员方法:
boolean | equals(Object obj) 指示一些其他对象是否等于此。 |
---|---|
protected void | finalize() 当垃圾收集确定不再有对该对象的引用时,垃圾收集器在对象上调用该对象。 |
int | hashCode() 返回对象的哈希码值。 |
String | toString() 返回对象的字符串表示形式。 |
通知垃圾收集器回收当前对象,释放空间。但是,垃圾回收器并不一定会立即执行对象的收集和空间的释放。
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方法的比较规则,方法的实现,可以自行实现。
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 。
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方法的重写,使用频率较高,所以提供了自动重写的方式
返回对象的哈希码值 在一定的程度上 可以将hash值理解为对象的内存空间的地址值。
如果两个对象的equals返回true 则他们的hashCode方法 返回的hash码值肯定也是相同的
如果两个对象的hash值相同,那么他们的equals方法不一定为true
在一般情况下,当我们创建一个类的时候,我们都需要重写equals和toString();当重写equals的同时,也必须重写hashCode
用abstract修饰的类 就是抽象类
用abstract修饰方法 就是抽象方法
1 抽象类不能被实例化
2 抽象类是用来被继承
3 可以通过多态来访问其中的成员
1 使用abstract关键字修饰
2 抽象方法不能有方法体
3 抽象方法是用来被重写的。
1 抽象类可以没有抽象方法
2 包含抽象方法的类一定是抽象类
抽象类的子类 要么是抽象类 要么就重写父类中的所有的抽象方法
成员变量:变量 常量
构造方法:有
构造方法存在的意义:就是方便子类去使用父类的成员数据
成员方法:抽象方法:限定子类中必须完成的一些特定的功能
非抽象方法:提高了代码的复用性
编写一个Employee类,声明为抽象类,包含如下三个属性: name, id, salary。提供必要的构造器和抽象方法: work()。
对于Manager类来说,他既是员工,还具有奖金(bonus)的属性。请使用继承的思想,设计CommonEmployee类和Manager类,要求类中提供必要的方法进行属性访问。
public abstract class Employee { private int id; private String name; private double salary; public Employee() { }
public Employee(String name, double salary) { this.name = name; this.salary = salary; }
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 Manager extends Employee { private double bonus; @Override public void work() { System.out.println("管理普通雇员。。。。。"); } }
public class CommonEmployee extends Employee{ @Override public void work() { System.out.println("认真工作。。。。。"); } }
抽象类体现的就是一种模板模式的设计,抽象列作为多个子类的通用模板,子类在抽象了的基础上进行扩展,改造,但是子类总体上会保留抽象类的行为方式。
解决问题:
当功能内部一部分实现是确定,一部分的实现时不确定,可以把不确定的部分 暴露出去 让子类区实现。
模板模式:在软件设计开发中,对于一个算法而言,整体步骤是固定的,通用,这些步骤我们就可以定义在父类中,但是其中某一些部分是易变的,此时可以将易变的部分抽取出来,定义成抽象,让子类来实现。
计算代码的执行时间:
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(); } }
接口一种公共的规范标准,只要符合规范标准,就可以通用。
java中的接口更多的体现在对行为的抽象。
java中接口的本质是契约 规范。
interface 和 class是处于同一级的。
public interface 接口名{}
接口中的成员方法:
只能是抽象方法,默认的修饰符 public abstract
接口中没有构造方法。
接口中的变量默认是常量 public static final
1 接口不能被实例化
2 接口是用来被实现的。
3 实现接口就要实现接口中的所有的抽象方法。 实现类要么是抽象的,要么实现接口的所有的抽象方法
jdk8 对接口的新特性 后面讲。
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); } }
多态的体现: 具体类的多态 抽象类的多态 接口的多态。
多态的前提:继承或实现
要有方法的重写:有父类(父接口)的引用指向子(实现)类的对象
类和类的关系:
继承关系 只能单继承 可以多层继承
类和接口的关系:
实现关系,可以单实现 也可以是多实现。 在实现的同时也可以继承一个类
接口和接口关系
接口可以继承接口 接口的继承可以是单继承 也可以是多重继承 还可以是多层继承
区别点 | 抽象类 | 接口 |
---|---|---|
定义组成 | 包含抽象方法的类 具体方法 变量 构造方法 | 抽象方法和常量 |
使用 | 子类继承抽象类 | 实现类实现接口 |
关系 | 抽象类实现接口 | 接口不能继承抽象类,可以继承多个接口 |
对象 | 通过抽象类的多态性产生实例对象 | 无 |
局限 | 只能单继承 | 没有 |
1 类名作为方法的形参
方法的形参是类类型 实参就是对应类型的对象 参数传递时 传递时对象的地址值
2 类名作为方法的返回值
方法的返回值是类类型,真正返回的是该类型的对象。实际返回的是对象的地址值
当在参数传递的时候 如果传递的是类类型 本质都是传递的对象的引用地址值
方法的形参是抽象类类型 实参子类对象 实际传递的是子类对象的地址值
方法的返回值 返回的是子类对象 实际也是子类对象的地址值
方法的形参是接口类型 实参实现类对象 实际传递的是实现类对象的地址值
方法的返回值时接口类型 返回的是实现类对象 实际也是实现类对象的地址值
运动员和教练:
需求: 现在有乒乓球运动员和篮球运动员,乒乓球教练和篮球教练。为了参加东京奥运会,其他国家的运动员进行交流,乒乓球运动员需要学习英语。篮球运动员需要学习日语
在这个案例中,那些是具体的类 那些是抽象类 那些是接口
分析:
乒乓球运动员
篮球运动员
教练
思路:
1 定义学习外语的接口 成员方法:学外语
2 定义抽象人类 成员变量:姓名 年龄 构造方法 无参 带参 成员方 getter/setter 吃饭 睡觉
3 定义教练类:继承人类 构造方法 抽象方法 教
4 定义运动员类 继承人类,构造方法 抽象方法 play
5 定义具体的类:
乒乓球运动员 篮球运动员 都需继承运动员 并实现学习外语的接口 实现抽象方法
乒乓球教练 篮球教练 都需要继承教练抽象类
内部类就是定义在类的内部的类
格式:
class 外部类{ 修饰符 class 内部类{ } } public class Outer {//外部类 public class inner{//内部类 } }
内部类的访问特点:
public class Outer {//外部类 private int num = 10; public class Inner{//内部类 private int x = 200;//内部类的成员变量 public void print(){//内部类的成员方法 System.out.println(num); } }
public void show(){ Inner inner = new Inner(); System.out.println(inner.x); inner.print(); }
public static void main(String[] args) { Outer outer = new Outer(); outer.show(); } }
内部类可以直接访问外部类的成员 包括私有成员
外部类要访问内部类的成员 必须创建对象
根据内部类定义的位置:定义在类中,和方法是同一级别
在外部内创建成员内部类的对象格式:
外部类.内部类 内部类对象名 = new 外部类().new 内部类(); Outer.Inner inner = new Outer().new Inner();
成员内部类的使用;
将一个类 设计为内部类的目的 ,大多数都是不想外界访问,,所以内部类的定义应该私有化,私有化之后 在提供一个可以让外界调用的方法,方法内部常见内部类对象并调用
public class Outer {//外部类 private int num = 10; private class Inner{//内部类 private int x = 200;//内部类的成员变量 public void print(){//内部类的成员方法 System.out.println(num); } }
public void show(){ Inner inner = new Inner(); System.out.println(inner.x); inner.print(); } }
public class OuterTest { public static void main(String[] args) { Outer outer = new Outer(); outer.show(); //在其他类中访问外部类中的内部类 //Outer.Inner inner = new Outer().new Inner(); } }
局部内部类定义在方法中的类
public class Outer {//外部类 private int num = 10; public void mehtod(){ int num2 = 20; class Inner{// 局部内部类在外界(外部类之外的类)是无法直接访问的 public void show(){ System.out.println(num); System.out.println(num2); } } Inner inner = new Inner(); inner.show(); } public static void main(String[] args) { Outer outer = new Outer(); outer.mehtod(); } }
对于局部内部类 在他所在的方法之外是无法访问的。他的访问的范围仅限于他所在的方法。在方法内要访问 需要创建内部类的对象
匿名内部类的前提:
存在一个类或者接口,这里的类可以使抽象类也可以是具体类
匿名内部类:
格式:new 类名(){
重写的方法
}
new 接口名(){
实现方法
}
public class DaoTest{ public static void main(String[] args) { DaoTest daoTest = new DaoTest(); daoTest.test(new Dao() { @Override public void add() { System.out.println("匿名内部类实现...."); } }); } public void test(Dao dao ){ dao.add(); } }