一、封装
什么叫封装?为什么要实现封装
我们在编程开发中,一个类可以有两个角色 1.类的调用者 2.类的实现者
我们可以想感受到,一个类的调用者对于类的实现者的依赖是非常大的,对你需要使用的类的学习成本会非常的高。
那么我们可以想象一个场景,一个类的实现者在后来迭代自己的代码中将之前定义的成员变量信息进行了改动,而这个类在之前已经被调用过,那么调用者再次使用这个类时就会出现错误了。
如何解决?
引出"封装"这个概念,即用"private"这个关键字进行封装的实现。
private的意义是“私有的”,一但成员变量/方法被private修饰了,只能在当前类当中使用这个成员变量/方法。
那么要怎样才可以访问?
我们需要提供public修饰的公开的get和set方法。
有了这两个方法后,我们就可以在类外调用公开的getter和setter方法,对需要调用的类的成员变量进行访问。
示例:
class Person {
private String name;//实例成员变量
private int age;
public void setName(String name){
//name = name;//不能这样写
this.name = name;//this引用,表示调用该方法的对象
}
public String getName(){
return name;
}
public void show(){
System.out.println("name: "+name+" age: "+age);
}
}
public static void main(String[] args) {
Person person = new Person();
person.setName("caocao");
String name = person.getName();
System.out.println(name);
person.show();
}
// 运行结果
caocao
name: caocao age: 0
所以:如果需要获取或者修改这个 private 属性, 就需要使用 getter / setter 方法。
二、继承
我们先来看一段代码:
// Animal.java
public class Animal {
public String name;
public Animal(String name) {
this.name = name;
}
public void eat(String food) {
System.out.println(this.name + "正在吃" + food);
}
}
// Cat.java
class Cat {
public String name;
public Cat(String name) {
this.name = name;
}
public void eat(String food) {
System.out.println(this.name + "正在吃" + food);
}
}
// Bird.java
class Bird {
public String name;
public Bird(String name) {
this.name = name;
}
public void eat(String food) {
System.out.println(this.name + "正在吃" + food);
}
public void fly() {
System.out.println(this.name + "正在飞 ︿( ̄︶ ̄)︿");
}
}
这个代码我们发现其中存在了大量的冗余代码
我们会发现,在以上定义的类中 Cat 和 Bird 都是 Animal。 Cat is a animal. Bird is a Animal. (这里不纠结英语语法的元音辅音o~)
是的,他们都有一种隐含的 is a 的关系。
所以,如果以后的多个类中包含可以作为 is a 的关系的类,我们就可以把他看作是继承。采用继承来实现它。
程序层面上:表达继承的关系:extends
继承的意义是什么?
- 代码的复用
- 降低了代码的冗余
class Animal {
public String name;
public Animal(String name) {
this.name = name;
}
public void eat(String food) {
System.out.println(this.name + "正在吃" + food);
}
}
class Cat extends Animal {
public Cat(String name) {
// 使用 super 调用父类的构造方法.
super(name);
}
}
class Bird extends Animal {
public Bird(String name) {
super(name);
}
public void fly() {
System.out.println(this.name + "正在飞 ︿( ̄︶ ̄)︿");
}
}
public class Test {
public static void main(String[] args) {
Cat cat = new Cat("小黑");
cat.eat("猫粮");
Bird bird = new Bird("圆圆");
bird.fly();
}
}
这个代码就是使用了继承,Cat类和Bird类都继承了Animal类。
其他特点
- Java属于单继承,也就是不能够一次通过extends继承多个类。
- 子类会继承父类的所有。(除了构造方法之外的所有的东西)
- 子类继承了父类,那么构造子类的时候,需要先帮父类进行构造。
- protected关键字,为了继承产生的,相当于受保护的private。因为被private关键字修饰后被继承在子类中也不能被访问。
- 访问限定修饰符权限比较:
private < default < protected < public
面试问题
this 和 super 关键字?
-
this代表的是当前对象的引用。super代表父类的引用
-
this.data:访问当前对象的成员变量
this.func() :访问当前对象的成员方法
this() :访问当前类的构造方法super.data:访问父类的成员变量
super.func():访问父类的成员方法
super():调用父类的构造方法
三、多态
多态其实就是一种思想
前置知识:
· 向上转型:将派生类的对象赋值给基类的引用!
三种向上转型的方式:
- 动态绑定:运行时多态
首先,父类引用引用子类对象的时候,这个引用所能访问的只有父类所包含的变量或者方法,不可以访问子类独有的特性。
什么情况下会发生动态绑定:
- 先要发生向上转型
- 通过父类的引用,调用子类重写的父类那个方法。
(在编译的时候调用的是父类的那个方法而运行时候又是运行出子类复写父类这个方法的结果)
整个过程中发生了动态绑定,也就是运行时多态。
- 了解方法的重写(方法名相同、参数列表相同、返回值相同)
注意:
要重写的方法,不能是被private修饰的
被final所修饰的方法,不能被重写
需要重写的方法的访问限定修饰符,子类的访问限定修饰符一定要大于或等于父类的访问限定修饰符
有了上面的向上转型, 动态绑定, 方法重写之后, 我们就可以使用 多态(polypeptide) 的形式来设计程序了
多态顾名思义, 就是 “一个引用, 能表现出多种不同形态”
父类引用引用子类对象,且父类和子类有同名的覆盖方法,通过父类的引用来调用同名的覆盖方法的时候,他有可能会表现出不同的行为,把这种表现出不同行为的这种过程/思想,叫做多态。
什么时候发生多态就取决于父类引用引用的是哪个子类对象。
//示例:
class Shape {
public void draw() {
// 啥都不用干
}
}
class Cycle extends Shape {
@Override
public void draw() {
System.out.println("○");
}
}
class Rect extends Shape {
@Override
public void draw() {
System.out.println("□");
}
}
class Flower extends Shape {
@Override
public void draw() {
System.out.println("♣");
}
}
/我是分割线//
// Test.java
public class Test {
public static void main(String[] args) {
Shape shape1 = new Flower();
Shape shape2 = new Cycle();
Shape shape3 = new Rect();
drawMap(shape1);
drawMap(shape2);
drawMap(shape3);
}
// 打印单个图形
public static void drawShape(Shape shape) {
shape.draw();
}
}
在这个代码中, 分割线上方的代码是 类的实现者 编写的, 分割线下方的代码是 类的调用者 编写的.
当类的调用者在编写 drawMap 这个方法的时候, 参数类型为 Shape (父类), 此时在该方法内部并不知道, 也不关注当前的 shape 引用指向的是哪个类型(哪个子类)的实例. 此时 shape 这个引用调用 draw 方法可能会有多种不同的表现(和 shape 对应的实例相关), 这种行为就称为 多态