6、面向对象编程
6.1面向对象编程基础
(1)面向对象概述
1.对象:在现实社会中随处可见的一种食物都是对象,对象是食物存在的实体。通常将对象分为动态和静态两种。静态即属性,动态即行为。
2.类:类就是同一类事物的统称。
3.面向对象程序设计的特点
3.1封装--封装是面向对象编程的核心思想。将对象的属性和行为封装起来,其载体为类,类通常对对象隐藏其实现细节,这就是封装思想。采用封装思想保证了类内部数据结构的完整性,应用该类的用户不能轻易地直接操作此数据结构,之恩能够执行类允许公开的数据,这样避免了外部操作对内部数据的影响,提高了程序的可维护性。
3.2继承--类与类之间的关系,继承是其一种,继承主要是利用特定对象之间的共有属性
3.3多态--将父类对象应用于子类的特征就是多态。
(2)类与对象
1.成员变量--在java中对象的属性也称为成员变量,成员变量的定义与普通变量的定义一样。语法 数据类型 变量名[= 值] 其中[= 值]是可选内容,定义变量时可以为其赋值也可以不赋值。
2.成员方法--在java中,成员方法对应于类对象的行为,它主要用来定义类可执行的操作,他是包含一系列语句的代码块。
2.1成员方法的定义--语法:[权限修饰符][返回值类型]方法名([参数类型 参数名])[throws 异常类型]{
…//方法体
return 返回值;
}
2.2成员方法的参数--调用方法时可以给该方法传递一个或多个值,传给内部方法叫实参,在方法内部接受实参的变量叫形参,形参的声明语法与变量的声明语法一样,形参只在方法内部有效
2.2.1值参数--表明实参和形参之间按值传递,当值参数被调用时编译器为形参分配存储单元,然后将对应的实参的值复制到形参中,由于是值类型的传递方式,所以在方法中对值类型的形参的修改不会影响到实参。
2.2.2引用参数--如果在给方法传递参数时,参数的类型是数组或其他的引用类型,那么在方法中对参数的修改会反映到原有的数组或其他引用类型上,这种类型的参数称之为引用参数。
2.2.3不定长参数--声明方法时,如果有若干个相同类型的参数,可以定义为不定长参数,
方法:权限修饰符 返回值类型 方法名(参数类型 …参数名)
2.3成员方法的使用
代码实现:
public class Leopard {
public voidgaze(String target) {// 凝视。目标是参数target
System.out.println("猎豹凝视:"+ target);
}
public voidrun() {// 奔跑
System.out.println("猎豹开始奔跑");
}
public booleancatchPrey(String prey) {// 捕捉猎物,返回捕捉是否成功
System.out.println("猎豹开始捕捉"+ prey);
returntrue;// 返回成功
}
public voideat(String meat) {// 吃肉,参数是肉
System.out.println("猎豹吃"+ meat);
}
public voidsleep() {// 睡觉
System.out.println("猎豹睡觉");
}
public staticvoid main(String[] args) {
Leopard liebao= new Leopard();
liebao.gaze("羚羊");
liebao.run();
liebao.catchPrey("羚羊");
liebao.eat("羚羊肉");
liebao.sleep();
}
}
结果:
猎豹凝视:羚羊
猎豹开始奔跑
猎豹开始捕捉羚羊
猎豹吃羚羊肉
猎豹睡觉
3.构造方法--构造方法是一个与类同名的方法,对象的创建就是通过构造方法完成的。
构造方法的特点:(1)构造方法没有返回类型,也不能定义为void(2)构造方法的名称要与本类的名称相同(3)构造方法的主要作用就是完成对象的初始化工作,他能把定义对象的参数传递给对象成员。
4.局部变量--在成员方法中定义一个变量,那么这个变量就称为局部变量,局部变量在方法中被执行时创建,方法结束时销毁。
5.局部变量的有效范围--局部变量的有效范围从该变量的声明开始到该变量的结束为止。
6.对象的创建--在java语言中使用new创建对象。语法 Test test =new Test();
7.访问对象的属性和行为--用户使用new操作符创建一个对象后,可以使用“对象.类成员”来获取对象的属性和行为。当对象获取类成员时,也相应的获取了对象的属性和行为
8.对象的销毁--java会对一下两种情况的对象进行销毁(1)对象引用超过其作用范围,(2)讲对象赋值为null。
9.this关键字--明确引用的是类成员还是局部变量或者方法参数等。
(3)static 关键字--由static 修饰的变量、常量和方法被称作静态变量、静态方法、静态常量。
1.静态变量--java程序中把共享的变量用static修饰,该变量就是静态变量。
2.静态常量--在java中用 final static 修饰一个成员变量,这个成员变量就是一个静态常量。
3.静态方法--java中用static修饰的方法就是静态方法。
4.静态代码块--用static修饰的代码区域为静态代码块。
(4)类的主方法--主方法是类的入口点,他定义了程序从何处开始;主方法提供对程序控制流向的控制;
主方法的语法:public static void main(String [] args){
//方法体
}
6.2面向对象核心技术
(1)类的封装
代码实现:
public classRestaurant4 {
privateCook2 cook = new Cook2();// 餐厅封装的厨师类
publicvoid takeOrder(String dish) {// 下单
cook.cooking(dish);//通知厨师做菜
System.out.println("您的菜好了,请慢用。");
}
publicString saySorry() {// 拒绝顾客请求
return"抱歉,餐厅不提供此项服务。";
}
publicstatic void main(String[] args) {
Restaurant4water = new Restaurant4();// 创建餐厅对象,为顾客提供服务
System.out.println("**请让厨师为我做一份香辣肉丝。***");
water.takeOrder("香辣肉丝");//服务员给顾客下单
System.out.println("**你们的厨师叫什么名字?***");
System.out.println(water.saySorry());//服务员给顾客善意的答复
System.out.println("**请让厨师给我切一点葱花。***");
System.out.println(water.saySorry());///服务员给善意的答复顾客
}
}
结果
**请让厨师为我做一份香辣肉丝。***
Tom Cruise洗蔬菜
Tom Cruise切葱花
Tom Cruise开始烹饪香辣肉丝
您的菜好了,请慢用。
**你们的厨师叫什么名字?***
抱歉,餐厅不提供此项服务。
**请让厨师给我切一点葱花。***
抱歉,餐厅不提供此项服务。
封装的思想就是对客户隐藏其细节,
(2)类的继承--及城市程序开发中重要的概念,使整个程序架构具备一定的弹性,减少开发周期,提高软件的可维护型和可扩展性。
1.extends关键字--java中让一个类继承另一个类,用extends关键字,语法:子类 extends 父类
2.方法的重写
2.1重写的实现--重写就是在子类中将父类的成员方法的名称保留,重新编写成员方法的实现内容,更改成员方法的存储权限,或是修改成员方法的返回值类型。在继承中还有一种特殊的重写方式,子类与父类的成员方法返回值,方法名称、参数类型及个数完全相同,唯一不同的世方法实现内容,这种特殊的重写方法叫重构。
2.2super关键字--用来在方法重写后调用父类的属性和方法。
3.所有类的父类——Object类--所有类都直接或间接的继承了java.lang.Object类,下面介绍几个重要方法
3.1getClass()方法--他会返回对象执行时的class实例,然后使用此实例调用getName方法获取类的名称
3.2toString()方法--将一个对象返回为字符串形式,会返回一个String实例
3.3equals()方法--比较的是两个对象的实际内容
(3)类的多态
1.方法的重载--在同一个类中允许同时存在一个以上的同名方法,只要这些方法的参数个数或类型不同即可。
代码实现:
public classOverLoadTest {
//定义一个方法
publicstatic int add(int a) {
returna;
}
//定义与第一个方法参数个数不同的方法
publicstatic int add(int a, int b) {
returna + b;
}
//定义与第一个方法相同名称、参数类型不同的方法
publicstatic double add(double a, double b) {
returna + b;
}
//定义一个成员方法
publicstatic int add(int a, double b) {
return(int) (a + b);
}
//这个方法与前一个方法参数次序不同
publicstatic int add(double a, int b) {
return(int) (a + b);
}
//定义不定长参数
publicstatic int add(int... a) {
ints = 0;
//根据参数个数循环操作
for(int i = 0; i < a.length; i++) {
s+= a[i];// 将每个参数的值相加
}
returns;// 将计算结果返回
}
publicstatic void main(String args[]) {
System.out.println("调用add(int)方法:" + add(1));
System.out.println("调用add(int,int)方法:" + add(1,2));
System.out.println("调用add(double,double)方法:" + add(2.1,3.3));
System.out.println("调用add(int a,double b)方法:" + add(1, 3.3));
System.out.println("调用add(doublea, int b) 方法:" + add(2.1, 3));
System.out.println("调用add(int...a)不定长参数方法:"+ add(1, 2, 3, 4, 5, 6, 7, 8, 9));
System.out.println("调用add(int...a)不定长参数方法:" + add(2, 3, 4));
}
}
结果:
调用add(int)方法:1
调用add(int,int)方法:3
调用add(double,double)方法:5.4
调用add(int a, double b)方法:4
调用add(double a, int b) 方法:5
调用add(int... a)不定长参数方法:45
调用add(int... a)不定长参数方法:9
2.向上转型--将具体类转换为抽象类
代码实现
classQuadrangle { //四边形类
publicstatic void draw(Quadrangle q) {//四边形类中方法
//SomeSentence
}
}
public classParallelogram extends Quadrangle {
publicstatic void main(String args[]) {
Parallelogramp=new Parallelogram();
draw(p);
}
}
3.向下转型--将抽象类转化为具体类
代码
classQuadrangle {
publicstatic void draw(Quadrangle q) {
//SomeSentence
}
}
public classParallelogram extends Quadrangle {
publicstatic void main(String args[]) {
draw(newParallelogram());
//将平行四边形类对象看作是四边形对象,称为向上转型操作
Quadrangleq = new Parallelogram();
Parallelogramp = (Parallelogram) q; // 将父类对象赋予子类对象
将父类对象赋予子类对象,并强制转换为子类型
//Parallelogramp = (Parallelogram) q;
}
}
4.instanceof关键字--判断是否一个类实现了某个接口,也可以用它来判断一个实例对象是否属于一个类
代码实现
class Quadrangle {
publicstatic void draw(Quadrangle q) {
//SomeSentence
}
}
class Square extends Quadrangle {
//SomeSentence
}
class Anything {
//SomeSentence
}
public class Parallelogram extendsQuadrangle {
publicstatic void main(String args[]) {
Quadrangleq = new Quadrangle(); // 实例化父类对象
//判断父类对象是否为Parallelogram子类的一个实例
if(q instanceof Parallelogram) {
Parallelogramp = (Parallelogram) q; // 进行向下转型操作
}
//判断父类对象是否为Parallelogram子类的一个实例
if(q instanceof Square) {
Squares = (Square) q; // 进行向下转型操作
}
//由于q对象不为Anything类的对象,所以这条语句是错误的
//System.out.println(q instanceof Anything);
}
}
(4)抽象类与接口
1.抽象类与抽象方法--定义抽象类时,需要使用abstract关键字
代码实现:
public abstract class Market {
publicString name;//商场名称
public String goods;//商品名称
public abstract void shop();//抽象方法,用来输出信息
}
public classTaobaoMarket extends Market {
@Override
publicvoid shop() {
//TODO Auto-generated method stub
System.out.println(name+"网购"+goods);
}
}
public classWallMarket extends Market {
@Override
publicvoid shop() {
//TODO Auto-generated method stub
System.out.println(name+"实体店购买"+goods);
}
}
/**
* 使用抽象类模拟“去商场买衣服”的案例,然后通过派生类确定到底去哪个商场买衣服,买什么样的衣服。
*/
public classGoShopping {
publicstatic void main(String[] args) {
Marketmarket = new WallMarket();// 使用派生类对象创建抽象类对象
market.name= "沃尔玛";
market.goods= "七匹狼西服";
market.shop();
market= new TaobaoMarket();// 使用派生类对象创建抽象类对象
market.name= "淘宝";
market.goods= "韩都衣舍花裙";
market.shop();
}
}
结果:
沃尔玛实体店购买七匹狼西服
淘宝网购韩都衣舍花裙
使用抽象类和抽象方法时,需要遵循以下原则
1)在抽象类中,可以包含抽象方法,也可以不包含抽象方法,但是包含了抽象方法的类必须被定义为抽象类
2)抽象类不能直接实例化,即使抽象类中没有声明抽象方法,也不能实例化
3)抽象类被继承后,子类需要实现其中所有的抽象方法
4)如果继承抽象类的子类也被声明为抽象类,则可以不用实现父类中所有的抽象方法
2.接口的声明及实现--接口是抽象类的延伸,接口中所有方法都没有方法体
代码实现:
interfacedrawTest { // 定义接口
publicvoid draw(); // 定义方法
}
// 定义平行四边形类,该类实现了drawTest接口
classParallelogramgleUseInterface implements drawTest {
publicvoid draw() { // 由于该类实现了接口,所以需要覆盖draw()方法
System.out.println("平行四边形.draw()");
}
}
// 定义正方形类,该类实现了drawTest接口
classSquareUseInterface implements drawTest {
publicvoid draw() {
System.out.println("正方形.draw()");
}
}
public classQuadrangleUseInterface { // 定义四边形类
publicstatic void main(String[] args) {
drawTest[]d = { // 接口也可以进行向上转型操作
newSquareUseInterface(), new ParallelogramgleUseInterface() };
for(int i = 0; i < d.length; i++) {
d[i].draw();// 调用draw()方法
}
}
}
结果
正方形.draw()
平行四边形.draw()
3.多重继承--java中实现多重继承,须使用接口
代码实现:
public interfaceIFather {// 定义一个接口
voidsmoking();// 抽烟的方法
voidgoFishing();// 钓鱼的方法
}
public interfaceIMather {// 定义一个接口
voidwatchTV();// 看电视的方法
voidcooking();// 做饭的方法
}
public classMe implements IFather, IMather {// 继承IFather接口和IMather接口
@Override
publicvoid watchTV() {// 重写watchTV()方法
System.out.println("我喜欢看电视");
}
@Override
publicvoid cooking() {// 重写cook()方法
System.out.println("我喜欢做饭");
}
@Override
publicvoid smoking() {// 重写smoke()方法
System.out.println("我喜欢抽烟");
}
@Override
publicvoid goFishing() {// 重写goFishing()方法
System.out.println("我喜欢钓鱼");
}
publicstatic void main(String[] args) {
IFatherfather = new Me();// 通过子类创建IFather接口对象
System.out.println("爸爸的爱好:");
//使用接口对象调用子类中实现的方法
father.smoking();
father.goFishing();
IMathermather = new Me();// 通过子类创建IMather接口对象
System.out.println("\n妈妈的爱好:");
//使用接口对象调用子类中实现的方法
mather.cooking();
mather.watchTV();
}
}
结果:
爸爸的爱好:
我喜欢抽烟
我喜欢钓鱼
妈妈的爱好:
我喜欢做饭
我喜欢看电视
4.区分抽象类与接口--抽象类是对根源的抽象,接口是对动作的抽象
抽象类与接口的不同 | ||
比较项 | 抽象类 | 接口 |
方法 | 可以有非抽象类 | 所有方法都是抽象方法 |
属性 | 属性中可以有非静态常量 | 所有的属性都是静态常量 |
构造方法 | 有构造方法 | 没有构造方法 |
继承 | 一个类只能继承一个父类 | 一个类可以同时实现多个接口 |
被继承 | 一个类只能继承一个父类 | 一个接口可以同时继承多个接口 |
(5)访问控制
1.访问控制符
Java语言中的访问控制符权限 | ||||
| Public | Protected | Default(缺省) | Private |
本类 | 可见 | 可见 | 可见 | 可见 |
本类所在包 | 可见 | 可见 | 可见 | 不可见 |
其他包中的子类 | 可见 | 可见 | 不可见 | 不可见 |
其他包中的非子类 | 可见 | 不可见 | 不可见 | 不可见 |
使用访问控制符时,需要遵循以下原则:
1)大部分顶级类都是用public修饰;
2)如果某个类主要用作其他类的父类,该类中包含的大部分方法只是希望被其子类重写,要不想被外界直接调用,则应该使用protected修饰;
3)类中的绝大部分属性都应该使用private修饰,除非一些static或者类似全局变量的属性,才考虑使用public修饰;
4)当定义的方法只是用于辅助实现该类的其他方法(即工具方法),应该使用private修饰;
5)希望允许其他类自由调用的方法应该使用public修饰。
2.java类包--类包不仅可以解决类名冲突问题,还可以在开发庞大的应用程序是,帮助开发者管理庞大的应用程序组件,方便软件复用
3.final关键字
3.1final类--定义为final不能被继承。
3.2final方法--final方法不能被重写,可以防止子类修改该类的定义与实现方式,同时定义final的方法执行效率远高于非final方法。
3.3final变量--用于变量声明,一旦该变量被设定,就不可以在改变该变量的值。
(6)内部类
1.成员内部类
1.1成员内部类的简介--在一个类中使用内部类,可以直接存取其所在类的私有成员变量。
语法:public class OuterClass{//外部类
private class InnerClass{//内部类
//……
}
}
1.2内部类向上转型为接口
代码实现:
interfaceOutInterface { // 定义一个接口
publicvoid f();
}
public classInterfaceInner {
publicstatic void main(String args[]) {
OuterClass2 out = new OuterClass2 (); // 实例化一个OuterClass2对象
//调用doit()方法,返回一个OutInterface接口
OutInterfaceoutinter = out.doit();
outinter.f();// 调用f()方法
}
}
classOuterClass2 {
//定义一个内部类实现OutInterface接口
privateclass InnerClass implements OutInterface {
InnerClass(Strings) { // 内部类构造方法
System.out.println(s);
}
publicvoid f() { // 实现接口中的f()方法
System.out.println("访问内部类中的f()方法");
}
}
publicOutInterface doit() { // 定义一个方法,返回值类型为OutInterface接口
returnnew InnerClass("访问内部类构造方法");
}
}
1.3使用this关键字获取内部类与外部类的引用--在外部类与内部类的成员变量名称相同时可以使用this关键字
2.局部内部类--可以在类的局部位置进行定义。
代码实现:
interface OutInterface2 { // 定义一个接口
}
class OuterClass3 {
publicOutInterface2 doit(final String x) { // doit()方法参数为final类型
//在doit()方法中定义一个内部类
classInnerClass2 implements OutInterface2 {
InnerClass2(Strings) {
s= x;
System.out.println(s);
}
}
returnnew InnerClass2("doit");
}
}
内部类被定义在了doit()方法内部。在doit()方法外部无法访问该内部类,但该内部类可以访问当前代码块的常量以及此外部类的所有成员
3.匿名内部类
代码实现
interfaceOutInterface2 { // 定义一个接口
}
classOuterClass4 {
publicOutInterface2 doit() { // 定义doit()方法
returnnew OutInterface2() { // 声明匿名内部类
privateint i = 0;
publicint getValue() {
returni;
}
};
}
}
使用匿名内部类应该遵循以下原则:
1)匿名类没有构造方法
2)匿名类不能定义静态的成员
3)匿名类不能用private、public、protected、static、final、abstract等修饰符
4)只可以创建一个匿名类实例。
4.静态内部类--在内部类前加static修饰,该内部类就是静态内部类(在程序开发中比较少见)
5.内部类的继承--需要设置专门的语法来完成
代码实现
public classOutputInnerClass extends ClassA.ClassB { // 继承内部类ClassB
publicOutputInnerClass(ClassA a) {
a.super();
}
}
class ClassA{
classClassB {
}
}
在某个类继承内部类时,必须硬性的给予这个类一个带参数的构造方法,并且该构造方法的参数必须是该内部类的外部类引用,就像例子中的ClassA a,同时在构造方法体重使用a.super()语句