您的位置:首页 > 移动开发 > Objective-C

面对对象4【抽象abstract】【接口interface】【多态】【instanceof关键字】【Object类】

2014-09-05 22:48 771 查看

一、抽象类(AbstractClass)

1、抽象:就是从多个事物中将共性的,本质的内容抽取出来。

简单说:就是看不懂的、模糊的、不具体的事物。

2、抽象类:表示具体功能不明确的,被abstract关键字修饰,且包含抽象方法的类。

3、抽象方法:只有功能声明,没有功能主体的方法称为抽象方法。

4、抽象类的特点:

①、抽象方法一定要定义在抽象类中。

②、抽象方法和抽象类都必须被abstract关键字修饰。

③、抽象类不可以用new创建对象。因为调用抽象方法没意义。

④、抽象类中的抽象方法要被使用,必须由子类覆写其所有的抽象方法后,建立子类对象调用。

如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类。

5、抽象类、抽象方法的定义格式:

abstract class 抽象类名

{

访问权限 abstract 返回值类型 方法名称(参数);

}

6、抽象类和一般类的区别:

①.抽象类比一般类多了抽象函数。就是在类中可以定义抽象方法。

②.抽象类不可以被实例化。

特殊:抽象类中可以不定义抽象方法,这样做仅仅是不让该类建立对象。

7、注意:在使用abstract关键字修饰抽象方法时不能使用private修饰。因为抽象方法必须要被子类覆写,而如果使用了private修饰,则子类是无法覆写的。

代码示例:

abstract class Student{
abstract void study(); //抽象方法。
}

class BaseStudent extends Student{
void study(){
System.out.println("base study");  //覆写抽象方法。
}
}

class AdvStudent extends Student{
void study(){
System.out.println("adv study");  //覆写抽象方法。
}
}

class AbstractDemo {
public static void main(String[] args) {
newBaseStudent().study();  //创建子类匿名对象并调用覆写后的方法。
}
}


二、接口(interface)

1、接口:一种特殊的抽象类,由全局常量和公共的抽象方法组成。

当抽象类中的方法都是抽象的,那么该类可以通过接口的形式来表示。

2、接口的定义格式:用interface关键字来定义

interface
接口名称


{

public static final返回值类型常量名 =初始化值; //全局常量

public abstract返回值类型抽象方法名(); //公共抽象方法

}

代码示例:

interface Inter
{
public static final int NUM = 3;
public abstract void show();
}


3、接口在定义时的格式特点:

①、接口中常见定义:常量、抽象方法。

②、接口中的成员都有固定修饰符。

常量:public static final

方法:public abstract

4、接口的注意事项:

①、接口中的成员都是public权限的,绝对不可以改变。

②、接口中全局常量前面的public static final和抽象方法前面的public abstract都是可以省略掉的,即使不写,系统默认也会给它加上。

③、接口不可以被创建对象,因为有抽象方法。

④、需要被子类实现,子类将接口中的抽象方法全都覆盖后,子类才可以实例化。

否则子类是一个抽象类。

⑤、接口可以被类多实现,也是对多继承不支持的转换形式。java支持多实现。

5、接口之间的关系

类和类之间是继承关系 —— extends

类和接口之间是实现关系 —— implements

接口和接口之间是继承关系。

示例代码:

interface A{
void methodA();
}
interface Bextends A{
void methodB();
}
interface Cextends B{
void methodC();
}
class Dimplements C{
pubilc void methodA(){}
pubilc void methodB(){}
pubilc void methodC(){}
}


注意:接口和接口之间支持多继承。因为接口里面都是抽象方法。

示例代码:

interface A{
void methodA();
}
interface B{
void methodB();
}
interface Cextends B,A{
void methodC();
}


6、接口的特点(和好处):

接口是对外暴露的规则。
接口是程序的功能扩展。
接口的出现降低耦合性(即:降低紧密联系程度)。
接口可以用来多实现。
类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口。
接口与接口之间可以有继承关系。

7、接口与抽象类的区别:

共性
都是不断抽取出来的抽象的概念

区别1
抽象类体现继承关系,一个类只能单继承

接口体现实现关系,一个类可以多实现

区别2
抽象类是继承,是 "is a "关系

接口是实现,是 "like a"关系

区别3
抽象类中可以定义非抽象方法,供子类直接使用

接口的方法都是抽象,接口中的成员都有固定修饰符

三、多态

1、多态:可以理解为事物存在的体现形态。

例如:人→ 男人、女人

动物 → 猫、狗

创建一个猫的对象可以通过以下两种方式:

猫 x = new 猫();

动物 x = new 猫();

2、多态的体现:

父类的引用指向了自己的子类对象。
//动物 x= new
猫();

父类的引用也可以接收自己的子类对象。(将父类的引用做为函数的参数进行传递)

3、多态的前提:

必须是类与类之间有关系。要么继承,要么实现。

通常还有一个前提:存在覆盖。

4、多态的好处:多态的出现大大的提高了程序的扩展性。

5、多态的弊端:提高了扩展性,但是只能使用父类的引用访问父类中的成员。

6、多态转型:

Java中多态性的体现形式主要有两种:

①、方法的重载和覆写

②、对象的多态性

对象的多态性主要分为两种类型:

向上转型:子类对象→父类对象。用子类去实例化父类对象,程序会自动完成。

向下转型:父类对象→子类对象。强制将父类的引用转成子类类型。

对象转型格式:

向上转型:父类父类对象 =子类实例;
//例如:Animal a = newCat();

向下转型:子类子类实例 = (子类)父类对象; //例如:Cat
c =(Cat)a;

注意:千万不要出现将父类对象转成子类类型的操作。

示例代码:

Animal a = newAnimal();

Cat c = (Cat)a;

我们能转换的是父类引用指向了自己的子类对象时,该引用可以被提升,也可以被前者转换。

总结一句话:多态自始至终都是子类对象在做着变化。

注意:在进行对象的向下转型前,必须首先发生对象的向上转型,否则将会出现对象类型转换异常。

示例代码:

abstract class Animal{
public abstract void eat();  //动物共有的行为方法。
}

class Cat extends Animal{
public void eat(){
System.out.println("吃鱼");
}
public void catchMouse(){   //猫特有的行为方法。
System.out.println("抓老鼠");
}
}

class Dog extends Animal{
public void eat(){
System.out.println("吃骨头");
}
public void kanJia(){  //狗特有的行为方法。
System.out.println("看家");
}
}

class DuoTaiDemo2{
public static void main(String[] args) {
Animal a = new Animal();
Cat c = (Cat)a;  //报错。会出现类型转换异常。

/*
正确的方式:
Animal a = new Cat();
Cat c = (Cat)a;
*/
}
}


报错原因:父类用其本身类实例化自己的对象,但它并不知道谁是自己的子类,在转换的时候肯定就会出现类型转换异常。

解决方式:只要将这两个对象建立好关系即可,即在声明父类对象时先发生向上转型,相当于是用子类去实例化父类对象,也就是说这个时候父类知道有这么一个子类。

7、多态中成员的特点:

①—1、多态中(非静态)成员函数的特点:

在编译时期:参阅引用型变量所属的类中是否有调用的方法,如果有,编译通过;如果没有则编译失败。

在运行时期:参阅对象所属的类中是否有调用的方法。

简单总结就是:成员函数在多态调用时,编译看左边,运行看右边。

示例代码:

class Fu{
void method1(){
System.out.println("fu method_1");
}
void method2(){
System.out.println("fu method_2");
}
}

class Zi extends Fu{
void method1(){
System.out.println("zimethod_1");
}
void method3(){
System.out.println("zimethod_3");
}
}

class DuoTaiDemo4 {
public static void main(String[] args) {
Fu f = new Zi();
f.method1();   //结果是zi method_1。打印的是被子类覆写之后的方法。
f.method2();
//f.method3();  //会报错,因为父类中没有method3这个方法。
}
}


个人总结:当发生向上转型之后,用父类对象调用的方法一定是被子类覆写之后的方法。

为什么上面示例中f.method1();的结果是子类中的zi method_1呢?

因为一般方法存在于内存的方法区的非静态方法区,是通过对象或者this、super等调用,而当用子类实例化父类对象的时候,再用父类对象去调用子父类中的同名方法,则相当于是以【new子类().方法名();】的形式在调用,所以运行的结果是子类中同名方法的结果。

①—2、多态中静态成员函数的特点:

无论编译和运行,都参考左边。

示例代码:

class Fu{
static void method4(){
System.out.println("fumethod_4");
}
}

class Zi extends Fu{
static void method4(){
System.out.println("zimethod_4");
}
}

class DuoTaiDemo4 {
public static void main(String[] args) {
Fu f = new Zi();
f.method4();   //结果是:fumethod_4。开发中一般不会出现,几乎不会去覆盖静态。面试中有可能遇到。
Zi z = new Zi();
z.method4();  //结果是:zimethod_4
}
}


为什么上面示例中f.method4();的结果是父类中的fu method_4呢?

因为当用子类去实例化父类对象的时候,JVM会将父类、子类都加载进内存,但是这个时候静态方法已经在内存中优先于对象存在了,也就是说这个时候需不需要对象都没什么特别的意义,父类对象调用的就是本类中的静态方法。

②、多态中成员变量的特点:

无论编译和运行,都参考左边(引用型变量所属的类)。

换句话说就是:当父类和子类中出现同名变量的时候,多态情况下看的是左边。

示例代码:

class Fu{
int num = 5;
}

class Zi extends Fu{
int num = 8;
}

class DuoTaiDemo4 {
public static void main(String[] args) {
Fu f = new Zi();
System.out.println(f.num);  //结果是:5

Zi z = new Zi();
System.out.println(z.num);  //结果是:8
}
}


四、instanceof关键字

1、作用:在Java中,可以用instanceof关键字判断一个对象到底是哪个类的实例。

2、使用格式:

对象 instanceof类 ——> 返回值类型是boolean型

示例代码:

public static void function(Animal a)  //将父类的引用做为函数的参数接收进来。
{
a.eat();
if(a instanceof Cat)  //判断传进来的对象是否是Cat类的对象。
{
Cat c = (Cat)a;  //如果是就执行向下转型操作。
c.catchMouse();	 //执行Cat类对象的特有行为方法。
}
else if(a instanceof Dog)
{
Dog d = (Dog)a;
d.kanJia();
}
}


五、Object类

1、Object类:是Java中所有类的直接或者间接的父类,位于Java继承体系的根节点。

一个类如果没有明确的继承一个类,则它肯定是Object的子类。

该类中所具备的功能是所有对象都具备的。

2、Object类中只有一个无参数的构造方法,所有没有明确父类的类默认都隐式的调用了Object类的构造方法。

3、所有的对象都可以向Object进行转换,即一切的引用数据类型都可以使用Object进行接收

4、Object类中的常用方法:

序号
方法名称
类型
描述
1
public Object()
构造
构造方法
2
public boolean equals(Object obj)
普通
对象比较
3
public int hashCode()
普通
取得哈希值
4
public String toString()
普通
对象打印时调用
equals()方法:Object类中equals()方法默认比较的是对象的内存地址值。

代码示例:

class Demo {}
class ObjectDemo{
public static void main(String[] args) {
Demo d1 = new Demo();
Demo d2 = new Demo();
Demo d3 = d1;

System.out.println(d1.equals(d3));  //结果是:true
System.out.println(d1.equals(d2)); //结果是:false
System.out.println(d1==d2);    //结果是:false
System.out.println(d1==d3);    //结果是:true
}
}


其实equal()方法底层的比较原理就是使用的比较运算符“==”

注意:

Object类中已经提供了对对象是否相同的比较方式。如果自定义类中也有比较相同的功能,没有必要重新定义。

只要沿袭父类中的功能,建立自己特有的比较内容即可。这就是覆盖。

示例代码:

class Demo{
private int num;
Demo(int num){
this.num= num;
}

public boolean equals(Object obj){     //在自定义类中覆写Object类中已经存在的比较方法以达到沿袭父类功能的效果。
if(!(obj instanceof Demo))     //加入判断来控制传入对象是否具有可比性。
return false;
Demo d = (Demo)obj;  //向下转型。
return this.num == d.num;
}
}

class Person{}

class ObjectDemo{
public static void main(String[] args) {
Demo d1 = new Demo(4);
Demo d2 = new Demo(4);

Person p = new Person();
System.out.println(d1.equals(p));    //两种不同类型的对象比较。会报错。类型转换异常。
}
}


toString()方法:将功能返回的结果以字符串形式返回。

Java中,对象输出时一定会调用Object类中的toString()方法,但是加与不加toString()的最终打印结果是一样的。

示例代码:

class Demo(){}
class ObjectDemo
{
public static void main(String[] args)
{
Demo d1 = new Demo();
System.out.println(d1);                  //结果是:Demo@18b3364
System.out.println(d1.toString());      //结果是:Demo@18b3364
}


/*

Demo@18b3364:Demo表示对象所属类的名称,@后面的部分是该对象在内存中的哈希地址值。

*/

//对象的哈希地址值可以通过Object类中的hashCode()方法获取。由于hashCode()方法返回的默认是int类型的值,所以需要被转换成16进制的形式才能跟上面的结果中的哈希值部分一样。

Class c =d1.getClass();
//获取Demo类的类文件对象。

System.out.println(c.getName());
//输出获取到的类文件的类名。

//通过hashCode()方法获取Demo类对象的哈希值,并转成16进制表现形式。

System.out.println(c.getName()+"@"+Integer.toHexString(d1.hashCode()));

//输出结果是:Demo@18b3364

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐