本节内容
1.对象与对象的关系
2.值传递/引用传递
3.抽象类
4.接口
5.多态
6.向上转型/向下转型
一、对象与对象的关系
Java中对象与对象的一对一关系 :
比如我们的一对夫妇他们之间的关系就是一对一,在Java中的一对一关系,实质是将一个类当做另一个类的属性。
package com.xk; /* *作者:吴志龙 *日期:2018年7月30日 */ public class Husband { String name; int age; //老公与妻子的一对一关系 Wife wife; }
package com.xk; /* *作者:吴志龙 *日期:2018年7月30日 */ public class Wife { String name; int age; //妻子与老公的一对一关系 Husband husband; }
Java中对象与对象的一对多关系 :
比如我们的部门与员工之间的关系,一个部门有多个员工,这是将一个员工数组当做部门类的一个属性。
package com.xk; /* *作者:吴志龙 *日期:2018年7月30日 */ public class Emp { int e_id; String name; int age; char sex; String address; double salary; }
package com.xk; /* *作者:吴志龙 *日期:2018年7月30日 */ public class Dept { int dept_id; String dept_name; //部门与员工的一对多关系 Emp[] emp; }
二、方法传值
首先要说明的是java中是没有指针的,java中只存在值传递,只存在值传递!!! 然而我们经常看到对于对象(数组,类,接口)的传递似乎有点像引用传递,可以改变对象中某个属性的值。但是不要被这个假象所蒙蔽,实际上这个传入函数的值是对象引用的拷贝,即传递的是引用的地址值,所以还是按值传递
值传递
public class Test3 { public static void change(int a){ a=50; } public static void main(String[] args) { int a=10; System.out.println(a); change(a); System.out.println(a); } }
运行结果: 10 10
引用传递
public class Test3 { public static void change(int []a){ a[0]=50; } public static void main(String[] args) { int []a={10,20}; System.out.println(a[0]); change(a); System.out.println(a[0]); } }
运行结果: 10 50
String
public class Test { public static void change(String s){ s="zhangsan"; } public static void main(String[] args) { String s=new String("lisi"); System.out.println(s); change(s); System.out.println(s); } }
运行结果: list list
三、抽象类
抽象类的定义
普通类是一个完善的功能类,可以直接产生实例化对象,并且在普通类中可以包含有构造方法、普通方法、static方法、常量和变量等内容。而抽象类是指在普通类的结构里面增加抽象方法的组成部分。
那么什么叫抽象方法呢?在所有的普通方法上面都会有一个“{}”,这个表示方法体,有方法体的方法一定可以被对象直接使用。而抽象方法,是指没有方法体的方法,同时抽象方法还必须使用关键字abstract做修饰。
而拥有抽象方法的类就是抽象类,抽象类要使用abstract关键字声明。
package com.xk; /* *作者:吴志龙 *日期:2018年7月30日 */ public abstract class Animal { //普通方法 public void sleep(){ System.out.println("有方法体"); } //抽象方法 没有方法体 使用 abstract 关键词修饰 public abstract void eat(); }
抽象类的使用:
抽象类不能实例化(不能创建对象) 为什么?????
因为抽象类中存在抽象方法,我们知道一个如果能实例化,通过实例化对象就可以调用类中的属性与方法,而抽象方法没有方法体,没有方法体就不能调用,既然不能调用方法,那么也就不能实例化。
抽象类的使用原则如下:
(1)抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public;
(2)抽象类不能直接实例化,需要依靠子类采用向上转型的方式处理;
(3)抽象类必须有子类,使用extends继承,一个子类只能继承一个抽象类;
(4)子类(如果不是抽象类)则必须覆写抽象类之中的全部抽象方法(如果子类没有实现父类的抽象方法,则必须将子类也定义为为abstract类。)
package com.xk; /* *作者:吴志龙 *日期:2018年7月30日 */ public abstract class Animal { //普通方法 public void sleep(){ System.out.println("有方法体"); } //抽象方法 没有方法体 使用 abstract 关键词修饰 public abstract void eat(); }
package com.xk; /* *作者:吴志龙 *日期:2018年7月30日 */ public class Dog extends Animal{ //dog类是Animal抽象类的子类,是一个普通类 //强制重写抽象方法 @Override public void eat() { // TODO Auto-generated method stub } }
现在就可以清楚的发现:
(1)抽象类继承子类里面有明确的方法覆写要求,而普通类可以有选择性的来决定是否需要覆写;
(2)抽象类实际上就比普通类多了一些抽象方法而已,其他组成部分和普通类完全一样;
(3)普通类对象可以直接实例化,但抽象类的对象必须经过向上转型之后才可以得到。
虽然一个类的子类可以去继承任意的一个普通类,可是从开发的实际要求来讲,普通类尽量不要去继承另外一个普通类,而是去继承抽象类。
三、抽象类的使用限制
(1)抽象类中有构造方法么?
由于抽象类里会存在一些属性,那么抽象类中一定存在构造方法,其存在目的是为了属性的初始化。
并且子类对象实例化的时候,依然满足先执行父类构造,再执行子类构造的顺序。
package com.xk; /* *作者:吴志龙 *日期:2018年7月30日 */ public abstract class Animal { //构造方法 public Animal() { System.out.println("Animal类的构造方法"); } //普通方法 public void sleep(){ System.out.println("有方法体"); } //抽象方法 没有方法体 使用 abstract 关键词修饰 public abstract void eat(); }
package com.xk; /* *作者:吴志龙 *日期:2018年7月30日 */ public class Dog extends Animal{ //dog类是Animal抽象类的子类,是一个普通类 public Dog() { System.out.println("Dog 类的构造方法"); } //强制重写抽象方法 @Override public void eat() { // TODO Auto-generated method stub } public static void main(String[] args) { //向上转型 Animal animal =new Dog(); } }
运行结果
Animal类的构造方法
Dog 类的构造方法
(2)抽象类可以用final声明么?
不能,因为抽象类必须有子类,而final定义的类不能有子类;
抽象类的应用:抽象类是模板式设计
例如,现在有三类事物:
(1)机器人:充电,工作;
(2)人:吃饭,工作,睡觉;
(3)猪:进食,睡觉。
现要求实现一个程序,可以实现三种不同事物的行为
四、接口
接口,英文称作interface,在软件工程中,接口泛指供别人调用的方法或者函数。从这里,我们可以体会到Java语言设计者的初衷,它是对行为的抽象
[public] interface InterfaceName { }
接口中可以含有 变量和方法。但是要注意,接口中的变量会被隐式地指定为public static final变量(并且只能是public static final变量,用private修饰会报编译错误),而方法会被隐式地指定为public abstract方法且只能是public abstract方法(用其他关键字,比如private、protected、static、 final等修饰会报编译错误),并且接口中所有的方法不能有具体的实现,也就是说,接口中的方法必须都是抽象方法。从这里可以隐约看出接口和抽象类的区别,接口是一种极度抽象的类型,它比抽象类更加“抽象”,并且一般情况下不在接口中定义变量。
定义:
Java接口(Interface),是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)
接口的特点:
- Java接口中的成员变量默认都是public,static,final类型的(都可省略),必须被显示初始化,即接口中的成员变量为常量(大写,单词之间用”_”分隔);
- Java接口中的方法默认都是public,abstract类型的(都可省略),没有方法体,不能被实例化;
- Java接口中只能包含public,static,final类型的成员变量和public,abstract类型的成员方法;
- 接口中没有构造方法,不能被实例化;
- 一个接口不能实现(implements)另一个接口,但它可以继承多个其它的接口
- Java接口必须通过类来实现它的抽象方法;
- 当类实现了某个Java接口时,它必须实现接口中的所有抽象方法,否则这个类必须声明为抽象类;
- 不允许创建接口的实例(实例化),但允许定义接口类型的引用变量,该引用变量引用实现了这个接口的类的实例;
- 一个类只能继承一个直接的父类,但可以实现多个接口,间接的实现了多继承。
现在有这样一个需求:
现在学校的招待环境: 招待的人员有: 学生 老师 家长
学生: 在食堂吃饭、在宿舍住宿
老师:教师食堂 、学校公寓
家长: 招待所吃饭 、招待所
理论上,当然可以对每个不同身份的人各定义一个对应的类,并实现各自的方法,但是观察这写类,可以归纳出其有一个共同的模板,即“人”的“食、宿”问题。这时候,就可以发挥接口的功能了

interface Person{ void eat(); void sleep(); } class Student implements Person{ public void eat(){ System.out.println("学生去食堂吃饭!"); } public void sleep(){ System.out.println("学生回寝室睡觉!"); } } class Teacher implements Person{ public void eat(){ System.out.println("教师去教工餐厅吃饭!"); } public void sleep(){ System.out.println("教师回学校公寓睡觉!"); } } class Parents implements Person{ publicvoid eat(){ System.out.println("家长去招待所饭馆吃饭!"); } public void sleep(){ System.out.println("家长回招待所睡觉!"); } } public class PersonInterface{ public static void main(String[] args) { Person p=new Student(); p.eat(); p.sleep(); p=new Teacher(); p.eat(); p.sleep(); p=new Parents(); p.eat(); p.sleep(); } }
现在需要添加一些功能,即现在需要添加“外宾、上级领导”两类角色,并且以后工具需要还要添加相应的身份角色的人进来,此时,只需要根据需要添加“外宾”类、“领导”类,而主类仍然可以拿来就用,无需进行更多的修改。此时就可以显示出接口的作用了。

class Foreign implements Person{ publicvoid eat(){ System.out.println("外宾去酒店吃饭!"); } public void sleep(){ System.out.println("外宾回酒店睡觉!"); } } class Leader implements Person{ publicvoid eat(){ System.out.println("领导去宾馆吃饭!"); } public void sleep(){ System.out.println("外宾回宾馆睡觉!"); } }
通过继承来扩展接口
通过继承,可以很容易地在接口中添加新的方法声明,还可以通过继承在新接口中组合数个接口。

interface Monster {// 怪物 void menace();// 威胁 } interface DangerousMonster extends Monster { void destroy();// 破坏 } interface Lethal {// 致命的 void kill();// 杀死 } class DragonZilla implements DangerousMonster { @Override public void menace() { // TODO Auto-generated method stub } @Override public void destroy() { // TODO Auto-generated method stub } } interface Vampire extends DangerousMonster, Lethal {// 吸血鬼 void drinkBlood(); } class VeryBadVampire implements Vampire { @Override public void menace() { // TODO Auto-generated method stub } @Override public void destroy() { // TODO Auto-generated method stub } @Override public void kill() { // TODO Auto-generated method stub } @Override public void drinkBlood() { // TODO Auto-generated method stub } } public class HorrorShow { static void u(Monster b) { b.menace(); } static void v(DangerousMonster d) { d.menace(); d.destroy(); } static void w(Lethal l) { l.kill(); } static void x(Vampire v) { v.menace(); v.destroy(); v.kill(); v.drinkBlood(); } public static void main(String[] args) { DangerousMonster barney = new DragonZilla(); u(barney); v(barney); Vampire vlad = new VeryBadVampire(); u(vlad); v(vlad); w(vlad); x(vlad); } }
DangerousMonster是Monster的直接扩展,它产生了一个新接口。DragonZilla中实现了这个接口。
在Vampire中使用的语法仅适用于接口继承。一般情况下,只可以将extends用于单一类,但是可以引用多个基类接口。
一 接口和抽象类的相似性
1 接口和抽象类都不能被实例化,它们都位于继承树的顶端,用于被其他类实现和继承。
2 接口和抽象类都可以包含抽象方法,实现接口或继承抽象类的普通子类都必须实现这些抽象方法。
二 接口和抽象类的区别
1 接口里只能包含抽象方法,静态方法和默认方法,不能为普通方法提供方法实现,抽象类则完全可以包含普通方法。
2 接口里只能定义静态常量,不能定义普通成员变量,抽象类里则既可以定义普通成员变量,也可以定义静态常量。
3 接口不能包含构造器,抽象类可以包含构造器,抽象类里的构造器并不是用于创建对象,而是让其子类调用这些构造器来完成属于抽象类的初始化操作。
4 接口里不能包含初始化块,但抽象类里完全可以包含初始化块。
5 一个类最多只能有一个直接父类,包括抽象类,但一个类可以直接实现多个接口,通过实现多个接口可以弥补Java单继承不足。
五、多态:
定义:多态:指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式
一种事物多种表现形态。
实现多态的技术称为:动态绑定(dynamic binding),是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法
多态存在的三个必要条件
一、要有继承;
二、要有重写;
三、父类引用指向子类对象。
Java中的多态表现有:
静态多态: 在编译阶段体现: 方法的重载
多态多态:在代码运行阶段体现: 方法的重写。
多态的好处:
提高了代码的维护性(继承保证)
提高了代码的扩展性(由多态保证)
六、向上转型/向下转型
要转型,首先要有继承。继承是面向对象语言中一个代码复用的机制,简单说就是子类继承了父类中的非私有属性和可以继承的方法,然后子类可以继续扩展自己的属性及方法
向上转型:子类对象转为父类,父类可以是接口。公式:Father f = new Son();Father是父类或接口,son是子类。
向下转型:父类对象转为子类。公式:Son s = (Son)f;

public class Human { public void sleep() { System.out.println("Human sleep.."); } public static void main(String[] args) { Human h = new Male();// 向上转型 h.sleep(); Male m = new Male();// 干嘛要向上转型 m.sleep(); // h.speak();此方法不能编译,报错说Human类没有此方法 } } class Male extends Human { @Override public void sleep() { System.out.println("Male sleep.."); } public void speak() { System.out.println("I am Male"); } } class Female extends Human { @Override public void sleep() { System.out.println("Female sleep.."); } public void speak() { System.out.println("I am Female"); } }
向上转型与向下转型可以看作是数据类型的转换:
向上转型:可以看作是小类型转大类型 自动转换
向下转型:可以看作是大类型转小类型 强制转换。
向下转型:注意看转的父类引用是不是这个子类的实例,如果不是就不能转,如果是才能转。