您的位置:首页 > 职场人生

黑马程序员-----面向对象的特性封装、继承、多态、抽象

2015-09-19 22:31 525 查看

------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

1.面向对象的特性

1.1封装

1.1.1封装的特点

1.直接对外部暴露成员变量是很不安全的,这时可以将成员变量“私有化”,对外提供公有的 get和set方法;

2.封装的好处:

1)隐藏实现细节,提供公共的访问方式

2)提高了代码的复用性

3)提高安全性。

3.封装的原则:

1)将不需要对外提供的内容都隐藏起来。

2)把属性隐藏,提供公共方法对其访问。

1.1.2封装的代码解释

/*

封装:

表现:

1,函数就是一个最基本封装体。

2,类其实也是一个封装体。

从以上两点得出结论:

好处:

1,提高了代码的复用性。

2,隐藏了实现细节,还要对外提供可以访问的方式。便于调用者的使用。这是核心之一,也可以理解为就是封装的概念。

3,提高了安全性。

它也是面向对象思想的特征之一。

共有三个特征:封装,继承,多态。

举例:机箱。隐藏了办卡设备的细节,对外提供了插口以及开关等访问内部细节的方式。

*/

//描述人。Person

//属性:年龄。

//行为:说话:说出自己的年龄。

/*

总结:

类中不需要对外提供的内容都私有化,包括属性和行为。

selectSort(int[] arr)

{

swap(

}

bubbleSort(int[] arr)

{

swap(

}

private swap(int[] arr,int a,int b)

{

}

重点:以后再描述事物,属性都私有化,并提供setXxxgetXxx方法对其进行访问。

*/

class Person
{
//属性:
private int age;//age就是被修饰为了private私有。也就是被隐藏了。这就是封装的一种体现。
//行为:
void speak()
{
System.out.println("age="+age);
}

/*
年龄已被私有,错误的值无法赋值,可是正确的值也赋值不了,不行。
咋办,按照之前所学习的封装的原理,隐藏后,还需要提供访问方式。
通过方法的方式,让其他程序访问到,就可以了。更重要的是可以在方法中加入逻辑判断。

记住:对变量的访问操作有两个动作:赋值(设置 set),取值(获取 get)
所以,对私有的变量访问的方式就是 set变量  get变量--> setAge  getAge
*/
//定义对age赋值的方法。
void setAge(int a)
{
//加入逻辑判断。
if(a>0 && a<130)
age = a;
else
//			System.out.println("对不起,您的年龄数值 "+a+" 是非法的。");
//			throw new RuntimeException("对不起,您的年龄数值 "+a+" 是非法的。");//异常!一旦出现,程序结束。需要修正代码。
}

//定义一个获取age值的方法。
int getAge()
{
return age;
}

}

class PersonDemo
{
public static void main(String[] args)
{
//测试Person.class。
//创建对象。
Person p = new Person();

/*
赋值-20是可以的,因为age属性是int类型,但是确不符合现实生活中的事物。
怎么解决这个问题呢?
不让它访问就哦了。怎么在代码上实现呢?需要使用一个Java中的关键字也是一个修饰符 private(私有,权限修饰符)
记住:私有仅仅是封装的体现形式而已。

*/
//		p.age = -20;//age不能在person类以外的程序中直接访问了。

//演示对age设置和获取方法的体现。
p.setAge(-20);
int a = p.getAge();
System.out.println("a="+a);
//		p.speak();
}
}


1.2继承

1.2.1继承的特点

1,继承的好处。★★★★★

2,Java中的单继承和多继承的区别,以及多继承的好处,为什么不直接支持?★★★★★

多继承好处:更加的扩展了子类的功能。这个机制非常好。

但是有弊端:导致调用多父类中的相同功能时,出现调用的不确定性。

3,对于继承体系,应该怎么学习?★★★★★

参阅顶层类,使用底层类。

4.使用关键字:extends

5.作用:代码重用。为多态提供了前提;

6.this和super的区别:

1).this:

1).在任何类中使用;

2).存储的是本类对象的引用;

3).可以访问本对象的成员变量、成员方法、构造方法;

2).super:

1).在子类中使用;

2).存储的是父类对象的引用;

3).可以访问父类对象的成员变量、成员方法、构造方法;

7.类的初始化过程:

加载class文件

堆中开辟空间

变量的默认初始化

变量的显示初始化

构造代码块初始化

构造方法初始化

成员变量-->构造代码块-->构造方法

8.Java中继承的特点:

1).Java中只能单继承;

2).Java中可以多级继承;

9.继承的好处和弊端:

好处:

1).代码复用

2).为多态提供了前提;

弊端:

1).由于继承,子类对父类产生了依赖;

1.2.2 继承的代码体现

/*
//描述学生。
class Student
{
//属性。
String name;
int age;

//行为。
void study()
{
System.out.println("good good study");
}
}

//描述工人。
class Worker
{
//属性。
String name;
int age;

//行为
void work()
{
System.out.println("hard work");
}
}
*/

/*
为了提高复用,只建立一份代码。
一个类只要和另一个类产生关系就可以了
关系:继承。
发现了获取到所需内容的同时也获取到不该具备的内容。
为什么?
发现原来这个两个类之间根本就不存在继承关系。

怎么解决呢?
找到学生和工人的共性类型。将需要提供复用的代码进行抽取。
定义到一个共性类型的类中。
Person name age。

怎么在代码体现中让学生和Person产生关系呢?
只要通过关键字 extends(继承) 就哦了。

*/

class Person
{
String name;
int age;

}

class Student extends Person//学生继承了Person 学生就是子类 Person就是父类(基类,超类)
{
void study()
{
System.out.println("good good study");
}
}
class Worker extends Person
{
void work()
{
System.out.println("hard work");
}
}
/*
面向对象 另一个特征:继承。
好处:提高了代码的复用性。让类与类产生了关系,给另一个特征 多态 提供了前提。

什么时候定义继承?
必须保证类与类之间有所属(is a)关系。 xxx是zzz中的一种。
苹果是水果中一种。狗是犬科中一种。

在Java中继承的体现:
Java允许单继承。不直接支持多继承,将多继承进行其他方式的体现。
单继承:一个子类只能有一个父类。
多继承:一个子类可以有多个父类。

*/

1.3抽象

1.3.1抽象特点

1,抽象类和抽象方法都需要被abstract修饰。抽象方法一定要定义在抽象类中。

2,抽象类不可以创建实例,原因:调用抽象方法没有意义。

3,只有覆盖了抽象类中所有的抽象方法后,其子类才可以实例化。

否则该子类还是一个抽象类。

1.3.2抽象类的细节

1,抽象类一定是个父类?

是的,因为不断抽取而来的。

2,抽象类是否有构造函数?

有,虽然不能给自己的对象初始化,但是可以给自己的子类对象初始化。

抽象类和一般类的异同点:

相同:

1,它们都是用来描述事物的。

2,它们之中都可以定义属性和行为。

不同:

1,一般类可以具体的描述事物。

抽象类描述事物的信息不具体

2,抽象类中可以多定义一个成员:抽象函数。

3,一般类可以创建对象,而抽象类不能创建对象。

3,抽象类中是否可以不定义抽象方法。

是可以的,那这个抽象类的存在到底有什么意义呢?仅仅是不让该类创建对象。

4.使用abstract关键字修饰;可以修饰“类”,可以修饰“成员方法”;

abstractclass A{

abstractvoid show();

}

5.“抽象类”的特点:

1).不能被实例化,只能用于被继承;

2).可以包含:成员变量、构造方法、成员方法、抽象方法;

3).可以不包含抽象方法;

6“抽象方法”的特点:

1).没有方法体;abstract void show();

2).必须被子类重写。除非子类也是个抽象类;

7子类继承抽象类使用关键字:extends,仍然是单继承;

8.一个子类继承了一个抽象类,必须实现抽象类中所有的抽象方法;

否则子类也必须是抽象的。

例如:

abstractclass A{

abstractvoid show();

}

classB extends A{ } //编译错误。类B不是抽象的,继承类抽象类必须重写抽象类中的所有抽象方法。

abstractclass B extends A{ } //编译通过。类B没有重写父类中的抽象方法,但类B是抽象的。

9.abstract关键字不能和哪些关键字共存:

1.private:抽象方法就是用来被子类重写的,而私有方法不能被子类重写;

2.final:抽象类和抽象方法就是用来被子类继承和重写的,而fianl类和final方法不能

被继承和重写;

3.static:static修饰的方法在没有任何对象的情况下就会被分配内存空间;而抽象方法

没有方法体,无法分配空间;

1.3.3抽象类代码体现

/*

需求:公司中程序员有姓名,工号,薪水,工作内容。

项目经理除了有姓名,工号,薪水,还有奖金,工作内容。

对给出需求进行数据建模。

在问题领域中先找寻其中涉及的对象。

程序员

属性:姓名,工号,薪水

行为:工作

项目经理

属性:姓名,工号,薪水,奖金

行为:工作

这些对象是否有关系呢?因为发现了他们之间的共性,应该存在着关系。

可以将他们的共性向上抽取到共性类型:员工。

员工:

属性:姓名,工号,薪水

行为:工作

发现员工的工作内容本身就不具体。应该是抽象的,由具体的子类来体现的。

一定要动手!

*/

abstract class Employee
{
private String name;
private String id;
private double pay;
/**
构造一个员工对象,一初始化就具备着三个属性。
*/
public Employee(String name,String id,double pay)
{
this.name = name;
this.id = id;
this.pay = pay;
}
/**
工作行为。
*/
public abstract void work();
}

//具体的子类:程序员。
class Programmer extends Employee
{
public Programmer(String name,String id,double pay)
{
super(name,id,pay);
}
public void work()
{
System.out.println("code....");
}
}

//具体的子类:经理。
class Manager extends Employee
{
//特有属性。
private double bonus;
public Manager(String name,String id,double pay,double bonus)
{
super(name,id,pay);
this.bonus = bonus;
}
public void work()
{
System.out.println("manage");
}
}

class AbstractTest
{
public static void main(String[] args)
{
System.out.println("Hello World!");
}
}

1.4多态

1.4.1多态的特点

体现:父类或者接口的引用指向了自己的子类对象。

好处:提高程序扩展性。

弊端:不能使用子类对象的特有内容。

前提:1,必须有关系(继承,实现);2,覆盖。

转型:

向上转型:提高扩展性,隐藏子类型,不需要使用子类型的特有方法。

向下转型:需要使用子类型的特有内容,注意:一定要instanceof判断类型,避免ClassCastExceptio

记住:转型中,自始至终都是子类对象做着类型的变化。

多态调用中,成员的特点:

成员变量,成员函数,静态函数。

总结:

对于成员变量和静态函数,编译和运行都看左边。

对于成员函数,编译看左边,运行看右边。原因是函数有覆盖,而是是动态绑 定到当前对象上。

1.4.2多态代码

//多态

class Dog extends Animal
{
public void eat()
{
System.out.println("骨头");
}
public void lookHome()
{
System.out.println("看家");
}
}

//描述猫
class Cat extends Animal
{
public void eat()
{
System.out.println("鱼");
}
public void catchMouse()
{
System.out.println("抓老鼠");
}
}
//进行抽取。将共性的功能抽取到父类Animal中。
abstract class Animal
{
public abstract void eat();
}

/*

多态:

【体现】

父类的引用或者接口的引用指向了自己的子类对象。

Dogd = new Dog();//Dog对象的类型是Dog类型。

Animala = new Dog();//Dog对象的类型右边是Dog类型,左边Animal类型。

【好处】

提高了程序的扩展性。

【弊端】

通过父类引用操作子类对象时,只能使用父类中已有的方法,不能操作子类特有的方法。

【前提】

1,必须有关系:继承,实现。

2,通常都有重写操作。

【子类的特有方法如何调用呢?】

Animala = new Dog();//Animal是父类型,new Dog()是子对象。

但是父类型引用指向子类对象时,这就是让子类对象进行了类型的提升(向上转型)。

向上转型好处:提高了扩展性,隐藏了子类型。弊端:不能使用子类型的特有方法。

如果要想使用子类的特有方法,只有子类型可以用。

可以向下转型,强制转换。

Animala = new Dog();

a.eat();

Dogd = (Dog)a;//将a转型为Dog类型。向下转型。

d.lookHome();

向下转型什么时候用?当需要使用子类型的特有内容时。

注意:无论向上还是向下转型,最终都是子类对象做着类型的变化。

【向下转型的注意事项】

Animala = new Dog();

//Catc = (Cat)a;向下转型因为不明确具体子类对象类型,所以容易引发ClassCastException异常。

所以为了避免这个问题,需要在向下转型前,做类型的判断。

判断类型用的是关键字 instanceof

if(ainstanceof Cat)//a指向的对象的类型是Cat类型。

{

//将a转型Cat类型。

Catc = (Cat)a;

c.catchMouse();

}

elseif(a instanceof Dog)

{

Dogd = (Dog)a;

d.lookHome();

}

【转型总结】

1,什么时候使用向上转型呢?

提高程序的扩展性,不关系子类型(子类型被隐藏)。

需要用子类的特有方法吗?不需要,哦了。向上转型。

2,什么时候使用向下转型呢?

需要使用子类型的特有方法时。

但是一定要使用 instanceof 进行类型的判断。避免发生ClassCastException

*/

class DuoTaiDemo2
{
public static void main(String[] args)
{
Dog d = new Dog();
//		d.eat();
//		d.lookHome();

/*
Animal a = new Dog();
a.eat();//可以的。
//		a.lookHome();//不可以的。
*/

method(d);
Cat c = new Cat();
method(c);
}
public static void method(Animal a)
{

a.eat();
//		Dog d = (Dog)a;//ClassCastException:类型转换异常。
//		d.lookHome();

//		a.lookHome();//不可以,因为动物不具备这个功能。
}

}


1.4.3多态成员的特点

/*

多态中,成员调用的特点。

1,成员变量。

当子父类中出现同名的成员变量时。

多态调用该变量时:

编译时期:参考的是引用型变量所属的类中是否有被调用的成员变量。没有,编译失败。

运行时期:也是调用引用型变量所属的类中的成员变量。

简单记:编译和运行都参考等号的左边。

编译运行看左边。

2,成员函数。

编译,参考左边,如果没有,编译失败。

运行,参考右边的对象所属的类。

编译看左边,运行看右边。

对于成员函数是动态绑定到对象上。

3,静态函数。

编译和运行都参考左边。

静态函数是静态的绑定到类上。

【结论】

对于成员变量和静态函数,编译和运行都看左边。

对于成员函数,编译看左边,运行看右边。

*/

class Fu
{
int num = 3;

void show()
{
System.out.println("fu show run");
}
static void method()
{
System.out.println("fu static method run");
}
}
class Zi extends Fu
{
int num = 5;

void show()
{
System.out.println("zi show run..");
}
static void method()
{
System.out.println("zi static method run");
}
}

class DuoTaiDemo3
{
public static void main(String[] args)
{
/*
//测试成员变量的多态调用。
Fu f = new Zi();
System.out.println(f.num);//3
Zi z = new Zi();
System.out.println(z.num);//5
*/
/*
//测试成员函数的多态调用。
Fu f = new Zi();
f.show();
*/
//测试静态函数的多态调用。
Fu f = new Zi();
f.method();
//注意:真正开发静态方法是不会被多态调用的,因为静态方法不所属于对象,而是所属于类。
Fu.method();
Zi.method();
}
}


/*
阶段一需求:笔记本电脑运行。
按照面向对象的思想,用代码体现。
名称提炼法。
笔记本电脑。
行为:运行。

class NoteBook
{
//运行功能。
public void run()
{
System.out.println("notebook run");
}
}

阶段二需求:想要在笔记本电脑上加上一个手握式鼠标。
多了个对象:鼠标。
行为:开启,关闭。
class Mouse
{
public void open()
{
System.out.println("mouse open");
}
public void close()
{
System.out.println("mouse close");
}
}
笔记本怎么用鼠标呢?
在笔记本中多一个使用鼠标的功能。
需要修改原来的笔记本类中的内容,添加一个功能。
class NoteBook
{
//运行功能。
public void run()
{
System.out.println("notebook run");
}
// 使用鼠标功能。
public void useMouse(Mouse m)
{
if(m!=null)
{
m.open();
m.close();
}
}
}
//问题:如果想要加入一个键盘呢?
只要描述一个键盘类,并在电脑类中加入一个使用键盘的功能就哦了。
但是发现从鼠标开始这个问题就已经产生了,一旦需要添加新设备的时候,
都需要改变电脑的源码。这个扩展性是非常差的。

设计上该如何改进呢?
之前的问题在于外围设备的增加和笔记本电脑之间的耦合性过高。
如何降低外围设备和笔记本电脑的耦合性呢?
外围设备还不确定,我们不要面对外围具体设备。
为了让笔记本可以使用这些设备,可以事先定义好一些规则,
笔记本只要使用这些规则就可以了。
有了这些规则就可以进行笔记本的功能扩展。
后期这些外围设备只要符合这些规则就可以被笔记本使用了。

那么规则在java中该如何体现呢?接口。

//1,描述接口。USB。

//2,描述笔记本电脑:运行功能,使用USB接口的功能。

*/
//USB接口定义。
interface USB
{
void open();
void close();
}

//描述笔记本电脑。
class NoteBook
{
public void run()
{
System.out.println("notebook run");
}

//使用usb接口的功能。
public void useUSB(USB usb)//接口类型的变量。接口类型的变量指向自己的子类对象。
//USB usb = new Mouse();
{
if(usb!=null)
{
usb.open();
usb.close();
}
}
}
//需要鼠标 。想要被笔记本电脑使用,该鼠标必须符合规则。
//描述鼠标。
class Mouse implements USB
{
public void open()
{
System.out.println("mouse open");
}
public void close()
{
System.out.println("mouse close");
}
}

class KeyBoard implements USB
{
public void open()
{
System.out.println("KeyBoard open");
}
public void close()
{
System.out.println("KeyBoard close");
}
}

/*
发现,接口的出现,
1,扩展了笔记本电脑功能。
2,定义了规则。
3,降低了笔记本电脑和外围设备之间的耦合性。
*/

class DuoTaiTest2
{
public static void main(String[] args)
{
NoteBook book = new NoteBook();
book.run();
book.useUSB(null);
book.useUSB(new Mouse());
book.useUSB(new KeyBoard());
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: