Java面向对象三大特性(封装继承多态)解释及案例
继承可以解决代码复用,让我们的编程更加靠近人类思维.当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过。测试类中添加一个方法showEmpAnnual(Employee e),实现获取任何员工对象的年工资,并在main方法中调用该方法[e.getAnnual()]简单的说:方法覆盖(重写)就是子类有
文章目录
包
包的三大作用
1.区分相同名字的类
2.当类很多时,可以很好的管理类[看Java API文档]
3.控制访问范围
包基本语法
package com.hspedu;
说明:
1 package 关键字,表示打包.
2 com.hspedu: 表示包名
包的本质分析(原理)
包的本质实际上就是创建不同的文件夹/目录来保存类文件,画出示意图。
看一个应用场景
现在有两个程序员共同开发一个java项目,程序员xiaoming希望定义一个类取名Dog,程序员xiaoqiang也想定义一个类也叫Dog.
两个程序员为此还吵了起来,怎么办? -》包
package src.use;
import src.com.gzx.Dog;
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
System.out.println(dog);
}
}
命名规则
只能包含数字、字母、下划线、小圆点,但不能用数字开头,不能是关键字或保留字
命名规范
一般是小写字母+小圆点一般是
com.公司名.项目名业务模块名
比如: com.hspedu.oa.model; com.hspedu.oa.controller;
举例:
com.sina.crm.user //用户模块
com.sina.crm.order //订单模块
com.sina.crm.utils //工具类
常用的包
一个包下,包含很多的类, java中常用的包有:
java.lang.* //lang包是基本包,默认引入,不需要再引入.
java.util.* //util包,系统提供的工具包,工具类,使用Scanner
java.net.* //网络包,网络开发
java.awt.* //是做java的界面开发,GUI
比如System.out.println(Math.abs(-1));
并不需要咱们手动导入import java.lang.Math.*;
导入包实例
package src.use;
// 我们需要使用到哪个类,就导入哪个类即可,不建议使用*导入
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
int[] arr = {1, 5, 6, 8, 2, 8, 8, 88, 7};
Arrays.sort(arr);
for (int i : arr
) {
System.out.print(i + "\t");
}
}
}
注意事项和使用细节
- package 的作用是声明当前类所在的包需要放在class的最上面,一个类中最多只有一句package
- import指令位置放在package的下面,在类定义前面,可以有多句且没有顺序要求。
访问修饰符
java提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限(范围)
1.公开级别: 用public 修饰,对外公开
2.受保护级别: 用protected修饰,对子类和同一个包中的类公开
3.默认级别: 没有修饰符号,向同一个包的类公开.
4.私有级别: 用private修饰,只有类本身可以访问,不对外公开.
4种访问修饰符的访问范围
使用的注意事项
1)修饰符可以用来修饰类中的属性,成员方法以及类
2)只有默认的和public才能修饰类! ,并且遵循上述访问权限的特点。
3)因为没有学习继承,因此关于在子类中的访问权限,我们讲完子类后,在回头讲解
4)成员方法的访问规则和属性完全一样.
面向对象编程-封装
封装(encapsulation)就是把抽象出的数据[属性]和对数据的操作[方法]封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作[方法],才能对数据进行操作。
封装的理解和好处
1)隐藏实现细节
2)可以对数据进行验证,保证安全合理
封装的实现步骤
1)将属性进行私有化private[不能直接修改属性]
2)提供一个公共的set方法, 用于对属性判断并赋值
public void setXxx(类型参数名){
//加入数据验证的业务逻辑
属性=参数名;
}
3)提供一个公共的get方法,用于获取属性的值
public XX getXxx(){ //权限判断
return XX;
}
那么在java中如何实现这种类似的控制呢?
请大家看一个小程序(Encapsulation01.java),不能随便查看人的年龄,工资等隐私,并对设置的年龄进行合理的验证。年龄合理就设置,否则给默认年龄,必须在1-120,年龄,工资不能直接查看,name的长度在 2-6之间
对所有属性设置 getXxx, setXxx方法, 限制在set方法里添加
面向对象编程-继承
继承可以解决代码复用,让我们的编程更加靠近人类思维.当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可。
继承的基本语法
class 子类 extends 父类{
}
1)子类就会自动拥有父类定义的属性和方法
2)父类又叫超类,基类。
3)子类又叫派生类。
继承的深入讨论/细节问题
- 子类继承了所有的属性和方法,但是私有属性和方法不能在子类直接访问,要通过公共的方法去访问
- 子类必须调用父类的构造器完成父类的初始化
- 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过[举例说明]
- 如果希望指定去调用父类的某个构造器,则显式的调用一下: super(参数列表)
- super在使用时,必须放在构造器第一行(super只能在构造器中使用)
- super() 和this()都只能放在构造器第一行, 因此这两个方法不能共存在一个构造器
- java所有类都是Object类的子类, Object是所有类的基类
- 父类构造器的调用不限于直接父类!将一直往上追溯直到Object类(顶级父类)
- 子类最多只能继承一个父类(指直接继承),即java中是单继承机制。
思考:如何让A类继承B类和C类? [ A 继承B, B继承C ] - 不能滥用继承,子类和父类之间必须满足is-a的逻辑关系
我是A类
hahah我是B类的有参构造
我是C类的有参构造
我是C类的无参构造
super关键词
super给编程带来的便利/细节
- 调用父类的构造器的好处(分工明确, 父类属性由父类初始化,子类的属性由子类初始化)
- 当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。
如果没有重名,使用super、this、 直接访问是一样的效果! - 使用super会跳过当前类,直接去父类查找,而
this.n1
或n1
会先从本类找,找不到再往他们的父类找
super和this的比较
方法重写/覆盖 (override)
简单的说:方法覆盖(重写)就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样那么我们就说子类的这个方法覆盖了父类的那个方法
注意事项和使用细节
方法重写也叫方法覆盖,需要满足下面的条件
-
子类的方法的参数,方法名称,要和父类方法的参数,方法名称完全一样。[演示]
-
子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类
比如父类返回类型是Object子类方法返回类型是String [演示]
public Object getInfo(){}
public String getInfo(){}
- 子类方法不能缩小父类方法的访问权限 [public > protected >默认> private]
void say0k() {}
public void say0k() {}
方法重载(overload)与重写(override)
面向对象编程-多态
Polymorphic
应用场景
请编写个程序,Master 类中有一个feed (喂食)方法, 可以完成主人给动物喂食物的信息。
方法或对象具有多种形态。是面向对象的第3大特征,多态是建立在封装和继承基础之上的。

多态的具体体现
1.方法的多态
重写和重载就体现多态
2.对象的多态
(1)一个对象的编译类型和运行类型可以不一致
(2)编译类型在定义对象时, 就确定了, 不能改变
(3)运行类型是可以变化的.
(4)编译类型看定义时=号的左边,运行类型看=号的右边

注意事项和细节讨论
多态的前提是:两个对象(类)存在继承关系
多态的向上转型
1)本质:父类的引用指向了子类的对象
2)语法:
父类类型 引用名= new 子类类型();
3)特点:编译类型看左边,运行类型看右边。
可以调用父类中的所有成员(需遵守访问权限) ,
不能调用子类中特有成员; 因为在编译阶段,能调用哪些成员,是由编译类型来决定的
最终运行效果看子类的具体实现!
Animal.java
public class Animal {
public void eat() {
System.out.println("动物吃食物");
}
}
Dog.java
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃屎");
}
public void infect() {
System.out.println("咬一口得狂犬病");
}
}
Test.java
import src.com.gzx.Animal;
import src.com.gzx.Dog;
public class Test {
public static void main(String[] args) {
Animal animal = new Dog();
// 运行效果看子类(运行类型)的具体实现,即调用方法时,按照从子类(运行类型)开始查找方法
animal.eat(); // 输出 狗吃屎
// 因为在编译阶段,能调用哪些成员,是由编译类型来决定的
// animal.infect(); //报错
}
}
多态的向下转型
1)语法:
子类类型 引用名=(子类类型) 父类引用;
2)只能强转父类的引用,不能强转父类的对象
3)要求父类的引用必须指向的是当前目标类型的对象
4)当向下转型后, 可以调用子类类型中所有的成员
public class Test {
public static void main(String[] args) {
Animal animal = new Dog();
// 运行效果看子类(运行类型)的具体实现,即调用方法时,按照从子类(运行类型)开始查找方法
animal.eat(); // 输出 狗吃屎
// 因为在编译阶段,能调用哪些成员,是由编译类型来决定的
// animal.infect(); //报错
// 如果想要调用Dod的infect()方法呢?
Dog dog = (Dog) animal;
dog.infect();
}
}
属性没有重写之说! 属性的值看编译类型
public class Test {
public static void main(String[] args) {
// 属性没有重写之说! 属性的值看编译类型
Base base = new Sub();// 向上转型
System.out.println(base.count);// ?看编译类型 10
Sub sub = new Sub();
System.out.println(sub.count);//?
}
}
class Base {
int count = 10;
}
class Sub extends Base {
int count = 20;
}
instanceof
比较操作符,用于判断对象的运行类型是否为XX类型或XX类型的子类型
public class Test {
public static void main(String[] args) {
BB bb = new BB();
System.out.println(bb instanceof AA);// true
System.out.println(bb instanceof BB);// true
AA aa = new BB();
System.out.println(aa instanceof AA);// true
System.out.println(aa instanceof BB);// true
}
}
class AA {
}
class BB extends AA {
}
Java的动态绑定机制
1.当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
2.当调用对象属性时,没有动态绑定机制,哪里声明,那里使用
public class Test {
public static void main(String[] args) {
A a = new B();// 向上转型
System.out.println(a.sum());// B里没有,调用父类A的, getI() + 10, getI是方法,用的B的20,+10,输出30
System.out.println(a.sum1());// B里没有,调用父类A的, i+1, 属性没有动态绑定机制, 所以, 10+10=20
}
}
class A {// 父类
public int i = 10;
public int sum() {
return getI() + 10;
}
public int sum1() {
return i + 10;
}
public int getI() {
return i;
}
}
class B extends A {// 子类
public int i = 20;
// public int sum() {
// return i + 20;
// }
public int getI() {
return i;
}
// public int sum1() {
// return i + 10;
// }
}
多态的应用1 多态数组
数组的定义类型为父类类型,里面保存的实际元素类型为子类类型
应用实例:现有一个继承结构如下:要求创建1个Person对象、2个Student 对象和2个Teacher对象,统一放在数组中,并调用每个对象say方法.
Person.java
package src.poly_.polyarr_;
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String say() {
String str = name + " " + age + "岁了";
return str;
}
}
Student.java
package src.poly_.polyarr_;
public class Student extends Person {
private double score;
public Student(String name, int age, double score) {
super(name, age);
this.score = score;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
@Override
public String say() {
return super.say() + " Student's score = " + score;
}
}
Teacher.java
package src.poly_.polyarr_;
public class Teacher extends Person {
private double salary;
public Teacher(String name, int age, double salary) {
super(name, age);
this.salary = salary;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
@Override
public String say() {
return super.say() + " Teacher's salary = " + salary;
}
}
PolyArray.java
package src.poly_.polyarr_;
public class PolyArray {
// 要求创建1个Person对象、2个Student 对象和2个Teacher对象,
// 统一放在数组中,并调用每个对象say方法.
public static void main(String[] args) {
Person p1 = new Person("pp", 11);
Student s1 = new Student("ss1", 1, 88.1);
Student s2 = new Student("ss2", 2, 88.2);
Teacher t1 = new Teacher("t1", 41, 8001);
Teacher t2 = new Teacher("t2", 42, 8002);
Person[] polyarr = {p1, s1, s2, t1, t2};
for (Person p : polyarr
) {
System.out.println(p.say());
}
}
}
应用实例升级:如何调用子类特有的方法,比如Teacher有一个teach , Student有一个study怎么调用?
多态的应用2 多态参数
方法定义的形参类型为父类类型,实参类型允许为子类类型
应用实例1:前面的主人喂动物
应用实例2: com.hspedu.poly.polyparameter包PloyParameter.java
定义员工类Employee,包含姓名和月工资[private], 以及计算年工资getAnnual的方法。
普通员工和经理继承了员工,
经理类多了奖金bonus属性和管理manage方法,
普通员工类多了work方法,
普通员工和经理类要求分别重写getAnnual方法
测试类中添加一个方法showEmpAnnual(Employee e),实现获取任何员工对象的年工资,并在main方法中调用该方法[e.getAnnual()]
测试类中添加一个方法,testWork ,如果是普通员工,则调用work方法,如果是经理,则调用manage方法
何调用子类特有的方法,比如Teacher有一个teach , Student有一个study怎么调用?
多态的应用2 多态参数
方法定义的形参类型为父类类型,实参类型允许为子类类型
应用实例1:前面的主人喂动物
应用实例2: com.hspedu.poly.polyparameter包PloyParameter.java
定义员工类Employee,包含姓名和月工资[private], 以及计算年工资getAnnual的方法。
普通员工和经理继承了员工,
经理类多了奖金bonus属性和管理manage方法,
普通员工类多了work方法,
普通员工和经理类要求分别重写getAnnual方法
测试类中添加一个方法showEmpAnnual(Employee e),实现获取任何员工对象的年工资,并在main方法中调用该方法[e.getAnnual()]
测试类中添加一个方法,testWork ,如果是普通员工,则调用work方法,如果是经理,则调用manage方法
更多推荐
所有评论(0)