封装
-
概述
将类的某些信息隐藏在类的内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的访问和操作。成员变量隐藏 (private),提供对应的 get/set 方法。 -
好处
- 安全:通过方法来控制成员变量,可以在方法中进行约束。例如 之前 创建的Dog对象 age 属性被设置成负数。
- 复用性:把代码用方法进行封装,提高了代码的复用性。
-
代码演示
public class Test { //成员变量private private int age ; //提供对应的 getAge/setAge 方法 public int getAge() { return age; } public void setAge(int age) { this.age = age; } } ---*--- 演示类,非标准类
继承
-
概述
使子类获得父类的属性和方法(包括构造方法、私有成员。),还可以在子类中扩充属性和方法。 -
格式
public class 子类名 extends 父类名 { }
public class Zi extends Fu { }//父类 class Fu{ public int age = 1; } //子类继承父类 class Zi extends Fu{ } //测试类 public class Demo { public static void main(String[] args) { Zi zi = new Zi(); System.out.println(zi.age);//继承了父类的 a } } ---*--- 输出结果: 1
-
继承的好处与弊端
-
好处:
1.增强的代码的复用性(多个类相同的成员可以放到一个类中)
2.提高了代码的维护性 (父类修改所有子类随之改变) -
弊端:
1.继承让类与类之间产生了关系,增强了类的耦合性。父类修改子类也会随之改变,削弱了子类的独立性 -
什么时候该用继承呢?
子类与父类的关系为: is a 。比如一个A类,一个B类。如果满足A是B的一种,或者B是A的一种,就说明他们存在继承关系,此时可以考虑使用继承,否则不能滥用。
举例:狗和动物,苹果和水果
-
-
注意事项
-
关于父类私有成员是否继承下来
//父类 class Fu { //父类的两个私有属性 private int a = 1; private int b = 2; //父类的一个私有方法 private void haha() { System.out.println("a=" + a); } void sop() { haha(); //调用父类私有方法 System.out.println("b=" + b); //访问父类私有成员变量b } } //子类 class Zi extends Fu { } //测试类 public class Test { public static void main(String[] args) { Zi zi = new Zi(); zi.sop(); } } ---*--- 输出结果: a=1 b=2
从输出结果可以看出,子类是继承了父类的私有成员的。否则调用sop方法的时候是找不到haha方法和变量b的。所以子类是可以继承父类所有的东西的,只是对于private等修饰符对外不可见,所以修饰符跟继承没有关系,只是影响属性或者方法对外是否可见 。
-
继承中成员变量的访问特点(super 关键字)
继承后子类对象可以直接访问所有的显性成员,所以这里主要说明一下父子类变量名同名情况。//父类 class Fu{ public int a = 1;//父类 a } //子类继承父类 class Zi extends Fu{ public int a = 2;//子类 a public void show(){ int a = 3; //局部 a System.out.println("局部a:" + a); System.out.println("this.a:" + this.a); System.out.println("super.a::" + super.a); } } //测试类 public class Demo { public static void main(String[] args) { Zi zi = new Zi(); zi.show();//调用子类 show 方法 } } ---*--- 输出结果: 局部a:3 this.a:2 super.a:1
子类对象通过方法访问一个变量时,会先在该方法中找,然后在子类成员中找,最后在父类成员范围找。如果要访问父类的同名变量,需要使用super关键字,super.a 代表访问父类的变量 a。
-
继承中成员方法的访问特点 (方法的重写)
同样的继承后子类对象可以直接访问所有的显性成员,所以这里主要说明一下父子类方法同名的情况。//父类 class Fu { void show(){ //父类show方法 System.out.println("父类show方法"); } } //子类 class Zi extends Fu { @Override //验证是否是重写 void show(){ //子类show方法 System.out.println("子类show方法"); super.show(); //调用父类show方法 } } //测试类 public class Son { public static void main(String[] args) { Zi zi = new Zi(); zi.show(); } } ---*--- 输出结果: 子类show方法 父类show方法
继承中出现父子类方法名和参数列表相同的时候,就构成了方法的重写,常用于在父类基础上添加新功能。 方法重写需要注意下列问题:
- 在子类方法上用 @Override 验证是否是重写。
- 访问被重写的show方法用super关键字访问
- 子类只能重写父类可见的方法 (否则被当做普通方法看待)
- 子类重写父类方法后访问权限不能低于父类方法
- 返回值、方法名、参数列表必须完全一致
- 重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常。
- 一旦方法被重写,子类只能通过super关键字访问被重写方法。
-
子类如何访问父类构造方法
//父类 class Fu { Fu(){ System.out.println("父类无参构造方法"); } } //子类 class Zi extends Fu { Zi(){ super();//可以不写系统会隐性给出 System.out.println("子类无参构造方法"); } } //测试类 public class Son { public static void main(String[] args) { Zi zi = new Zi(); } } ---*--- 输出结果: 父类无参构造方法 子类无参构造方法
所有的对象创建时都会自动调用构造方法,在继承中所有子类构造方法都会默认调用父类无参构造方法 super();。
访问子类构造方法:this(参数)
访问父类构造方法:super(参数) -
类只能单继承,但是可以多级继承
-
多态
-
概述
多态是同一个行为具有多个不同表现形式或形态的能力,是对象多种表现形式的体现。
多态就是同一个接口,使用不同的实例而执行不同操作,如图所示:
现实中,比如我们按下 F1 键这个动作:
如果当前在 Flash 界面下弹出的就是 AS 3 的帮助文档;
如果当前在 Word 下弹出的就是 Word 帮助;
在 Windows 下弹出的就是 Windows 帮助和支持。
同一个事件发生在不同的对象上会产生不同的结果。 -
多态的3个前提条件
1.继承(如果不继承就不能执行3)
2.重写(如果不重写就没有意义)
3.父类引用变量存放子类引用。
-
多态演示
//Animal类 class Animal { void eat(){} } //Dog类 class Dog extends Animal{ @Override void eat() { System.out.println("狗吃骨头"); } //子类独有方法 void work(){ System.out.println("狗看家"); } } public class Demo { public static void main(String[] args) { //父类引用变量存放子类引用 Animal a = new Dog(); a.eat(); a.work(); //编译报错 //类型转换 if (a instanceof Dog){ Dog d = (Dog)a; d.work(); } } } ---*--- 输出结果: 狗吃骨头 狗看家
注意:多态的写法,对象只能访问父类有的成员(否则编译报错),如果访问的是成员方法看该方法是否被重写(一般都会重写),重写则调用重写方法,否则调用父类方法。如果要访问子类特有成员,可以使用强制类型转换。但在这之前需要使用 instanceof 进行验证 a 对象是否是 Dog 类型的实例。
-
多态向上向下转型
向上转型:Animal a = new Dog(); //安全的
向下转型:Dog d = (Dog)a; //需要使用 instanceof 验证 -
多态的实现方式
- 普通类方法重写
- 抽象类
- 接口