1 在多态中访问子类特有成员
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();
}
}
将父类的引用转换为子类的引用对象,就能够访问到子类特有的方法了。
1.1 多态的转型
基本类型的类型转换:
自动类型转换:小的数据类型可以自定转换为大的数据类型
强制类型转换:可以把大的数据类型强制转换为小得数据类型,与可能造成数据的精度损失。
java中对象的强制类型转换称为造型。
-
从子类到父类类型的转换可以自动进行
-
从父类到子类的类型转换必须通过造型(强制转换)实现
-
无继承关系的引用类型间的转换是非法
Cat cat = (Cat) ac;
cat.play();
Dog dog = (Dog)ad;
dog.work();
1.2 instanceof
作用就是检验某一个引用是否是另一个类的实例对象。 返回值是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("类型不匹配");
}
将有子类到父类的类型转换 称为向上转型
将有父类到子类的类型转换 称为向下转型,需要强制转换
1.3 继承成员变量和继承方法的区别
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方法显示对象的面积(注意方法的参数类型,利用动态绑定技术)。
2 . 面向对象的三大特征在JDK中的体现
-
java.lang.Object
-
public class Object
Object 类是所有的java类的根父类。他是java中所有类的直接或间接的超类(基类)
如果在类的声明中,没有使用extends 关键字来指明其父类,则默认的父类就是Object
public class Animal extends Object {
2.1 Object类的主要的结构
构造方法:
-
-
Object()
-
成员方法:
-
-
boolean
equals(Object obj)
指示一些其他对象是否等于此。protected void
finalize()
当垃圾收集确定不再有对该对象的引用时,垃圾收集器在对象上调用该对象。int
hashCode()
返回对象的哈希码值。String
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 抽象类的特点
1 抽象类不能被实例化
2 抽象类是用来被继承
3 可以通过多态来访问其中的成员
3.3. 抽象方法的特点
1 使用abstract关键字修饰
2 抽象方法不能有方法体
3 抽象方法是用来被重写的。
3.4 抽象类和抽象方法的关系
1 抽象类可以没有抽象方法
2 包含抽象方法的类一定是抽象类
3.5 抽象类的子类
抽象类的子类 要么是抽象类 要么就重写父类中的所有的抽象方法
3.6 抽象类的成员特点
成员变量:变量 常量
构造方法:有
构造方法存在的意义:就是方便子类去使用父类的成员数据
成员方法:抽象方法:限定子类中必须完成的一些特定的功能
非抽象方法:提高了代码的复用性
-
编写一个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("认真工作。。。。。");
}
}
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
接口中没有构造方法。
接口中的变量默认是常量 public static final
5.3 接口的特点
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);
}
}
多态的体现: 具体类的多态 抽象类的多态 接口的多态。
多态的前提:继承或实现
要有方法的重写:有父类(父接口)的引用指向子(实现)类的对象
5.5. 接口和类的关系
类和类的关系:
继承关系 只能单继承 可以多层继承
类和接口的关系:
实现关系,可以单实现 也可以是多实现。 在实现的同时也可以继承一个类
接口和接口关系
接口可以继承接口 接口的继承可以是单继承 也可以是多重继承 还可以是多层继承
5.6.抽象类和接口的区别
区别点 | 抽象类 | 接口 |
---|---|---|
定义组成 | 包含抽象方法的类 具体方法 变量 构造方法 | 抽象方法和常量 |
使用 | 子类继承抽象类 | 实现类实现接口 |
关系 | 抽象类实现接口 | 接口不能继承抽象类,可以继承多个接口 |
对象 | 通过抽象类的多态性产生实例对象 | 无 |
局限 | 只能单继承 | 没有 |
6 参数传递
6.1 使用类名作为形参和返回值
1 类名作为方法的形参
方法的形参是类类型 实参就是对应类型的对象 参数传递时 传递时对象的地址值
2 类名作为方法的返回值
方法的返回值是类类型,真正返回的是该类型的对象。实际返回的是对象的地址值
当在参数传递的时候 如果传递的是类类型 本质都是传递的对象的引用地址值
6.2 抽象类做为形参和返回值
方法的形参是抽象类类型 实参子类对象 实际传递的是子类对象的地址值
方法的返回值 返回的是子类对象 实际也是子类对象的地址值
6.2 使用接口作为形参和返回值
方法的形参是接口类型 实参实现类对象 实际传递的是实现类对象的地址值
方法的返回值时接口类型 返回的是实现类对象 实际也是实现类对象的地址值
6.3 综合案例
运动员和教练:
需求: 现在有乒乓球运动员和篮球运动员,乒乓球教练和篮球教练。为了参加东京奥运会,其他国家的运动员进行交流,乒乓球运动员需要学习英语。篮球运动员需要学习日语
在这个案例中,那些是具体的类 那些是抽象类 那些是接口
分析:
乒乓球运动员
篮球运动员
教练
思路:
1 定义学习外语的接口 成员方法:学外语
2 定义抽象人类 成员变量:姓名 年龄 构造方法 无参 带参 成员方 getter/setter 吃饭 睡觉
3 定义教练类:继承人类 构造方法 抽象方法 教
4 定义运动员类 继承人类,构造方法 抽象方法 play
5 定义具体的类:
乒乓球运动员 篮球运动员 都需继承运动员 并实现学习外语的接口 实现抽象方法
乒乓球教练 篮球教练 都需要继承教练抽象类
7 内部类
内部类就是定义在类的内部的类
格式:
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();
}
}
内部类可以直接访问外部类的成员 包括私有成员
外部类要访问内部类的成员 必须创建对象
7.1 成员内部类
根据内部类定义的位置:定义在类中,和方法是同一级别
在外部内创建成员内部类的对象格式:
外部类.内部类 内部类对象名 = 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();
}
}
7.2 局部内部类
局部内部类定义在方法中的类
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();
}
}
对于局部内部类 在他所在的方法之外是无法访问的。他的访问的范围仅限于他所在的方法。在方法内要访问 需要创建内部类的对象
7.3. 匿名内部类
匿名内部类的前提:
存在一个类或者接口,这里的类可以使抽象类也可以是具体类
匿名内部类:
格式: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();
}
}