面向对象(十三)
面向对象(十三)
封装
简单的说,封装就是使用private将属性私有,然后提供get/set方法获取和设置该属性
在idea中,生成get/set方法快捷键:win(alt+insert), Mac(command+n),然后在列表中选择get/set
封装的作用
- 提高代码的安全性,保护数据
- 隐藏代码的实现细节
- 统一接口
- 增加系统的可维护性
package com.oop.demo03;
public class Person {
private String name;
private int age;
private char sex;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
// 对属性设置增加判断
public void setAge(int age) {
if (age>120 || age<0) {
System.out.println("输入的年龄异常!");
} else {
this.age = age;
}
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
}
/*
Application:
//一个项目只有一个main方法
public class Application {
public static void main(String[] args) {
Person ps1 = new Person();
ps1.setName("小明");
String name = ps1.getName();
System.out.println(name);
ps1.setAge(150);
int age = ps1.getAge(); // 输入的年龄异常!
System.out.println(age); // 0
}
}
*/
继承
super的使用
super注意点
- super调用父类的构造方法,必须在构造方法的第一个
- super必须只能出现在子类的方法或构造方法中
- super和this不能同时调用构造方法
super和this对比
- 代表的对象不同:super代表的是父类对象的引用,this代表本身调用者这个对象
- 前提不同:super必须在继承关系下才能使用,this没有继承关系也能使用
- 构造方法:this()是本类的构造,super()是父类的构造
重写
idea中重写方法快捷键,Win(Alt+Insert), Mac(command+N) 在选项中选择Override
重写需要有继承关系,是子类对父类方法的重写!
- 重写方法的方法名必须与父类的相同
- 参数列表必须相同
- 修饰符:修饰范围可以扩大但不能缩小:Public > Protected > Default > Private
- 抛出的异常:范围可以缩小,但不能放大:ClassNotFoundException(小) --> Exception(大)
重写,子类的方法必须和父类保持一致,但是方法体不同!
方法重写的作用
父类的方法,子类不一定需要,也不一定满足。
示例
下面Student类中调用了父类Person的构造方法,并重写了doWork方法
Application
package com.oop;
import com.oop.demo04.Student;
//一个项目只有一个main方法
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.test();
student.name = "小明";
student.doWork();
}
}
/*
运行结果:
Person...
Student...
Person: null正在工作。
Student: 小明正在工作。
*/
Student
package com.oop.demo04;
public class Student extends Person{
public Student() {
super(); //调用父类的构造方法
System.out.println("Student...");
}
public void test() {
super.doWork(); //使用super调用父类的方法
}
//重写父类的方法
@Override
public void doWork() {
System.out.println("Student: "+this.name+"正在工作。");
}
}
Person
package com.oop.demo04;
public class Person {
public Person() {
System.out.println("Person...");
}
public String name;
public int age;
protected void doWork() {
System.out.println("Person: "+this.name+"正在工作。");
}
}
补充:重写和重载的区别
- 重写必须有继承关系,而重载不需要
- 重载的参数或返回值需要与原方法不同;重写则需要保持一致
多态
现在有B类继承自A类,并且B中重写了A的test()方法,另外拥有一个父类没有的test2()方法
B
package com.oop.demo05;
public class B extends A{
@Override
public void test() {
System.out.println("BBB");
}
public void test2() {
System.out.println("Only BBB");
}
}
A
package com.oop.demo05;
public class A {
public void test() {
System.out.println("AAA");
}
}
Application
package com.oop;
import com.oop.demo05.A;
import com.oop.demo05.B;
//一个项目只有一个main方法
public class Application {
public static void main(String[] args) {
//对象的示例类型由右边决定
//对象可以使用那些方法由左边决定,与右边无关
B b1 = new B();
b1.test();
A b2 = new B();
b2.test();
//b2.test2(); //无法调用B(子类)中独有方法
Object b3 = new B();
}
}
/*
运行结果:
BBB
BBB
AAA
*/
需要注意的点
-
一个对象的实际类型是确定的,即new右侧的类型;A a = new A();这里的A()
-
但是可以指向的引用类型是不确定的:父类的引用类型可以指向子类 A a = new B();
-
B类可以调用的方式都是自己的或继承自父类的;而A可以指向子类型(即使用子类中重写的方法test()),但是不能使用子类的独有方法(test2())
对象能执行哪些方法,主要看对象左边的类型,和右边关系不大!
如果子类重写了父类的方法,则父类在引用时回执行子类重写的方法(A a = new B())。
多态使用的条件
从上可以得出,使用多态需要具备一下三个条件:
- 具有继承关系
- 子类重写父类的方法
- 父类引用指向子类对象
instance of方法判断一个对象是否是一个类的实例
2021-2-18更新,看了廖雪峰老师的博客,对多态的使用场景有了个更具体的认识
参考廖雪峰官网Java教程https://www.liaoxuefeng.com/wiki/1252599548343744/1260455778791232
现在有父类Person,定义了吃饭的方法,返回饭量;子类Man和Woman都继承Person类。
Person
package com.oop.demo08;
public class Person {
public int fan;
public int getFan() {
return fan;
}
public void setFan(int fan) {
this.fan = fan;
}
}
Man 设置饭量为原始Person的三倍
package com.oop.demo08;
public class Man extends Person{
@Override
public void setFan(int fan) {
this.fan = fan*3;
}
@Override
public int getFan() {
return this.fan;
}
}
Woman 设置饭量为原始Person的二倍
package com.oop.demo08;
public class Woman extends Person{
@Override
public void setFan(int fan) {
this.fan = 2*fan;
}
@Override
public int getFan() {
return this.fan;
}
}
我们在Application中定义一个方法来统计需要准备的总饭量,参数是Person实例的数组
Application
package com.oop;
import com.oop.demo08.Man;
import com.oop.demo08.Person;
import com.oop.demo08.Woman;
//一个项目只有一个main方法
public class Application {
public static void main(String[] args) {
Person p1 = new Man();
p1.setFan(5);
Person p2 = new Woman();
p2.setFan(3);
Person[] ps = {p1, p2};
int sumEat = eatTotal(ps);
System.out.println(sumEat);
}
public static int eatTotal(Person[] ps) {
int sum = 0;
for (Person p: ps) {
sum+=p.getFan();
}
return sum;
}
}
可以看到,在统计总的饭量的方法中,我们不用去管传入的Person是Man还是Woman,Java在运行时会自动调用对应的实例方法或重写的方法,最后统计出总饭量。这里的精髓在于eatTotal方法的参数,是Person,而Man和Woman都是Person。