目录
今日良言:你应该去爱这山,这水,这世间万物,以及自己
一、封装
二、继承
三、多态
今日良言:你应该去爱这山,这水,这世间万物,以及自己
一、封装
1.1.封装的思想
在我们写代码的时候会涉及两种角色:类的实现者和类的调用者
封装的本质就是让类的调用者不必太多的了解实现者是如何实现类的,只要知道如何使用类即可,这样就降低了类使用者的学习和和使用成本,从而降低了复杂程度。
上面是专业术语,我所理解的封装大概是这个意思:
举个栗子: 手机制造商生产了一部手机,而你买了这部手机,作为一个普通用户,你并不需要知道手机是如何生产的以及各种底层电路如何实现,你只需要知道如何使用手机即可,在这个例子中,手机制造商就是类的实现者,而你就是类的调用者。
1.2.java中使用private实现封装
被public修饰的成员变量或方法,可以被类的调用者直接使用
而private修饰的成员方法和变量,不可以被类的调用者直接使用
如下代码:使用get 和 set 方法访问私有的数据成员
class Student{
private String name;
public int age;
//提供一个公开的接口
public String getName() {
return this.name;
}
public void setName(String name){
this.name = name;
}
}
public class text{
public static void main(String[] args) {
//实例化一个对象
Student student = new Student();
student.age = 18;
student.setName("帅哥");
System.out.println(student.getName());
}
}
通过setName方法,我们可以对私有属性name进行初始化,然后再通过getName方法可以得到初始化后的私有属性name
1.3.在IDEA中提供了getter和setter方法
IDEA中可以使用快捷键 getter和setter方法,来直接自定义生成getName 和 setName 如下 :先定义一个类
class Student{
private String name;
public int age;
}
然后在类的内部:按住Alt+Insert,会弹出如下框再进行如下操作
然后就会生成如下代码:
封装对于一个项目工程来说,是很有必要的,这样对于代码的保护会有很大的帮助,所以,要多使用封装(与set 和 get方法搭配使用)。
2.继承
2.1继承的思想
继承使用关键字 extends A extends B
A叫做子类(派生类),B叫做父类(超类、基类)
2.2代码实现
如下一段代码:
class Animal {
public String name;
public void eat() {
System.out.println(this.name+"Animal:eat()");
}
public void sleep() {
System.out.println("Animal:sleep()");
}
public Animal(String name) {
this.name = name;
}
}
class Cat extends Animal {
public Cat(String name) {
super(name); // 显示调用 必须放到第一行
}
}
这里的Animal是父类,Cat是子类,继承是 is a 的关系 , Cat is an Animal
继承的优点:可以达到代码复用的效果。
子类访问父类的属性或者方法可以使用super关键字,这里总结一下super关键字和this关键字的区别:
super (父类对象的引用)
1.super(); 调用父类的构造方法,必须放在第一行,必须在子类的构造方法中
2.super.data 访问父类的成员属性
3.super.func 调用父类的成员方法
this(当前对象的引用)
1.this(); 调用本类的其他构造方法,必须是在本类构造方法里面,且必须是第一行。
2.this.data 访问本类的成员属性
3.this.func 调用本类的成员方法
2.3继承相关注意点
1.子类继承了父类除构造方法外的所有(字段+方法)。
2.子类在构造的时候必须先帮父类构造。
3.一个子类只能继承一个父类。
4.父类的私有属性和方法子类可以继承,但是无法访问
5.多重继承(一般不超过三层)
如下代码:
class Animal {
public String name;
public void eat() {
System.out.println(this.name+"Animal:eat()");
}
public void sleep() {
System.out.println("Animal:sleep()");
}
public Animal(String name) {
this.name = name;
}
}
class Cat extends Animal {
public Cat(String name) {
super(name); // 显示调用 必须放到第一行
}
}
class MiMi extends Cat {
public MiMi(String name) {
super(name);
}
}
6.被final修饰的类,不能被继承。
如下图:
3.多态
在理解多态之前,我们先了解下面这些知识点
3.1向上转型
父类引用,引用子类的对象,发生向上转型以后,通过父类对象的引用只能访问父类有的属性和方法
代码:
class Animal {
public String name;
public void eat() {
System.out.println(this.name+"Animal:eat()");
}
public void sleep() {
System.out.println("Animal:sleep()");
}
public Animal(String name) {
this.name = name;
}
}
class Cat extends Animal {
public Cat(String name) {
super(name); // 显示调用 必须放到第一行
}
}
public class TextDemo {
public static void main(String[] args) {
Animal animal = new Cat("初一");
animal.eat();
}
}
3.2发生向上转型的时机
1.直接赋值
下面的代码就是直接赋值发生向上转型
Animal animal = new Cat("初一");
2.传参
如下代码:
public class TextDemo {
public static void func(Animal animal) {
animal.eat();
}
public static void main(String[] args) {
Cat cat = new Cat("十三");
func(cat);
}
}
实例化一个子类对象,然后将该子类对象的引用cat当作参数传递给func函数时,发生向上转型。
3.返回值
如下代码:
public class TextDemo {
public static Animal func() {
return new Cat("初一");
}
public static void main(String[] args) {
Animal animal = func();
animal.eat();;
}
}
3.3运行时绑定(动态绑定)
父类引用,引用子类对象,通过父类引用调用同名的重写方法。
如下代码:
class Animal {
public String name;
public void eat() {
System.out.println(this.name+"Animal:eat()");
}
public void sleep() {
System.out.println("Animal:sleep()");
}
public Animal(String name) {
this.name = name;
}
}
class Cat extends Animal {
public Cat(String name) {
super(name); // 显示调用 必须放到第一行
}
public void eat() {
System.out.println("喵喵喵......");
}
}
public class TextDemo {
public static Animal func() {
return new Cat("初一");
}
public static void main(String[] args) {
Animal animal = func();
animal.eat();;
}
}
这里在子类中重写了eat方法,当运行上述代码时,打印的是 喵喵喵.... 此时发生的是运行时绑定,因为直至编译的时候这个eat方法还是父类的eat方法。
注:重写和重载的区别
重写(覆盖、覆写):
1.不同的类,发生在继承关系上
2.方法名相同
3.返回值相同
4.方法的参数列表相同
重载
1.在同一个类中
2.方法名相同
3.返回值不做要求
4.参数列表不同(参数个数或参数类型不同)
关于重写需要注意的事项:
1.需要重写的方法,不能被final修饰,因为被final修饰以后是密封方法,不可以修改
2.被重写的方法,访问修饰限定符不能是私有的
3.被重写的方法,子类的访问修饰限定符大于等于父类的访问修饰限定符
4.被static修饰的方法不能被重写
3.4 多态
铺垫了这么多,终于引出了我们的主角:多态。
为了更好的理解多态,举个例子:
假如你和你老婆养了一只小猫(初一),并且你们有一个两岁的儿子,你老婆平时都管他们叫儿子。有一天,你老婆对你说:“去给儿子喂点吃的”。 如果这里的“儿子”指的是小猫,那么你就要喂猫粮,如果指的是两岁的儿子,就要喂奶。 如何确定这里的“儿子”具体指谁,就需要考虑上下文来判断了。多态也是如此,一个引用具体是指父类对象,还是子类对象(可能有多个),就需要我们根据上下文判断了。
多态其实是一种思想。
多态指的是:在父类中定义的的属性和方法被子类继承以后,可以具有不同的数据类型或者表现出不同的行为,这使得同一个属性或者方法在父类及其各个子类中具有不同的含义。
多态发生的条件:
1.父类引用,引用子类对象
2.子类有和父类同名的重写方法
3.通过父类引用,调用子类这个同名的重写方法的时候,发生多态。
代码展示:
class Shape {
public void draw() {
}
}
class Circle extends Shape {
@Override
public void draw() {
System.out.println("画一个○");
}
}
class Flower extends Shape {
@Override
public void draw() {
System.out.println("画一个❀");
}
}
class React extends Shape {
@Override
public void draw() {
System.out.println("画一个□");
}
}
public class text {
public static void drawMap(Shape shape) {
shape.draw();
}
public static void main(String[] args) {
Shape shape1 = new Flower();
Shape shape2 = new React();
Shape shape3 = new Circle();
drawMap(shape1);
drawMap(shape2);
drawMap(shape3);
}
}
如上代码:当类的调用者在调用drawMap这个方法的时候,参数类型为Shape (父类),此时,在该方法内部并不知道,也不关注当前的shape引用指向的是哪个子类的实例。这个shape引用调用draw方法可能会有多种不同的表现(和 shape对应的实例相关),这种行为就称为多态