java封装、继承和多态
面向对象变成三大特征:封装、继承和多态
封装(encapsulation)
封装(encapsulation)就是把抽象出的数据[属性]和对数据的操作[方法]封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作[方法],才能对数据进行操作。(把属性和方法封装)
eg:打开电视,调节音量(内部很复杂),对电视机的操作(用户很简单)就是封装
封装的理解和好处
- 隐藏实现细节 : 方法(连接数据库)<--调用(传入参数..)
- 可以对数据进行验证,保证安全合理
封装的实现步骤(三步)
- 将属性进行私有化private【不能直接修改属性】(隐藏细节)
- 提供一个公共的(public)set方法,用于对属性判断并赋值
public void setXxx(类型参数名){//Xxx表示某个属性
//加入数据验证的业务逻辑
属性=参数名;
}
- 提供一个公共的(public)get方法,用于获取属性的值
public 数据类型 getXxx(){//权限判断,Xxx某个属性
return xx;
}
例子:
//我们可以将set写在构造器中,可以起到防护机制
package encap;
public class Encap01 {
public static void main(String[] args) {
Person p1=new Person();
p1.setName("xx");
p1.setAge(2000);
p1.setSalary(2000000);
System.out.println(p1.info());
//如果直接使用构造器指定属性,set失效了
new Person("smith",200,500000);
}
}
class Person{
public String name;//名字公开
private int age ;//年龄和薪水私有
private double salary;
public Person(){
}
public Person(String name, int age, double salary) {
this.name = name;
this.age = age;
this.salary = salary;
//我们可以将set写在构造器中,可以起到防护机制
setName(name);
setAge(age);
setSalary(salary);
}
public String getName() {
return name;
}
//封装过程
//可以直接快捷键生成getter & setter
public void setName(String name) {
//加入数据校验,增加业务逻辑
if(name.length()>=2 && name.length()<=6){
this.name = name;
}else{
System.out.println("name set error.set default");
this.name="无名";
}
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age>=1 && age<=120){
this.age = age;
}else{
System.out.println("年龄设置错误,需要在(1-120),给默认年龄18");
this.age=18;
}
}
public double getSalary() {
//可以增加对当前对象的权限范围
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
///写一个方法返回属性信息
public String info(){
return "name:"+name+" sarlary:"+salary;
}
}
继承
两个类的属性和方法有很多相同==>继承
继承可以解决代码复用,让我们的编程更加靠近人类思维.当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可。
示意图:
继承基本语法:
class 子类 extends 父类{
}
- 子类就会自动拥有父类定义的属性和方法
- 父类又叫超类,基类。
- 子类又叫派生类。
package extend.improve;
//父类,Pupil FUFEI
public class Student {
//公有方法:
public String name;
public int age;
private double score;
public void setScore(double score) {
this.score = score;
}
public void showInfo(){
System.out.println("学生:"+name+"age:"+age);
}
}
package extend.improve;
//继承student类
public class Graduate extends Student {
public void testing(){//和小学生不一样
System.out.println("学生:"+name+"正在考大学数学.......");
}
}
继承细节讨论
- 子类继承了所有的属性和方法,但是私有属性和方法不能在子类直接访问(可间接访问),要通过父类提供的公共的方法去访问
- 子类必须调用父类的构造器,完成父类的初始化
- 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过
如果父类只有无参构造器,子类初始化的时候会调用他;如果父类有参数的构造器,子类初始化的时候,一定要用super()指定构造器:
super("ss",10);
- 如果希望指定去调用父类的某个构造器,则显式的调用一下: super(参数列表)
- super在使用时,必须放在构造器第一行(super只能在构造器中使用)
- super()和 this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
- java所有类都是Object类的子类, Object是所有类的基类.
- 父类构造器的调用不限于直接父类!将一直往上追溯直到Object类(顶级父类)
- 子类最多只能继承一个父类(指直接继承),即java中是单继承机制。
思考:如何让A类继承B类和C类?(A->B->C) - 不能滥用继承,子类和父类之间必须满足is-a的逻辑关系(猫继承动物)
继承的本质
用子类查找一个属性时,按照查找关系,向上查找(private不能直接访问)
super关键字
super代表父类的引用,用于访问父类的属性、方法、构造器
- 访问父类的属性,但不能访问父类的private属性super.属性名;
- 访问父类的方法,不能访问父类的private方法super.方法名(参数列表);
- 访问父类的构造器:
super(参数列表);只能放在构造器的第一句,只能出现一句!
在继承关系中,可以用super访问父类的属性和方法:super.name;super.test(),但是不能访问私有类型。
super()只能在构造器第一行使用,用于指定参数的构造器
super带来的便利:
- 调用父类的构造器的好处(分工明确,父类属性由父类初始化,子类的属性由子类初始化)
- 当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super、this、直接访问是一样的效果!
- super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;如果多个基类(上级类)中都有同名的成员,使用super访问遵循就近原则。A->B->C
方法重写/覆盖(override)
基本介绍
方法覆盖(重写)就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么我们就说子类的这个方法覆盖了父类的方法。
子类按照自己的需求重写父类的方法,只有方法块能变
多态
方法或对象具有多种形态。建立在封装和继承基础上
多态的具体体现:
- 方法的重载:重写和重载
重载:传入不同的参数,调用不同的方法,体现多态 - 对象的多态
(1)一个对象的编译类型和运行类型可以不一致
(2)编译类型在定义对象时,就确定了,不能改变
(3)运行类型是可以变化的.
(4)编译类型看定义时=号的左边,运行类型看=号的右边
Animal animal = new Dog(); //【animal编译类型是Animal,运行类型Dog】
(父类的引用指向子类的对象)
animal = new Cat(): 【animal的运行类型变成了Cat,编译类型仍然是 Animal)
多态细节
多态的前提:两个对象(类)存在继承关系
多态的向上转型:(把子类对象向上转型)
1)本质:父类的引用指向了子类的对象
2)语法:父类类型 引用名=new 子类类型();
3)特点:编译类型看左边,运行类型看右边。可以调用父类中的所有成员(需遵守访问权限),不能调用子类中特有成员
Animal animal=new Cat()
√多态的向下转型
1)语法:子类类型 引用名 = (子类类型) 父类引用
2)只能强转父类的引用,不能强转父类的对象
3)要求父类的引用必须指向的是当前目标类型的对象
4)当向下转型后,可以调用子类类型中所有的成员
Cat cat = (Cat) animal;
另一个引用,也是指向cat对象
父类和子类都有个eat(),向上转型是让父类能直接用子类的eat()方法,而向下转型是为了让父类能用父类没有,但是子类有的fly()
animal对象只能调用父类的,
只有向上转型过的对象才能向下转型,向下转型为恢复子类所有功能
Animal animal=new Cat()
->向上转型
Cat cat = (Cat) animal;
->向下转型
Animal 是 Cat的父类,向上转型后,animal的编译类型是父类Animal,指向的是一个子类Cat实例;为了能用这个cat实例的他有但是父类没有的方法,就再来个引用,这个引用的编译类型是cat,让他指向Cat,就能用Cat的自己的方法。animal 和 cat指向的都是这个Cat实例,但是编译类型不一样,所能访问的方法和属性就不一样。
属性没有重写之说!属性的值看编译类型
public class poly01 {
public static void main(String[] args) {
Base base=new Sub();//向上转型
System.out.println(base.count);//结果为10,因为base的编译类型为Base
Sub sub=new Sub(); //sub.count=20
System.out.println(sub.count);//结果为20
Sub sub1=(Sub) base;
System.out.println(sub1.count);//结果为20
}
}
class Base{//父类
int count=10;
}
class Sub extends Base{//子类
int count=20;
}
instanceOf 比较操作符,用于判断对象的运行类型是否为XX类型或XX类型的子类型
动态绑定机制
- 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
- 当调用对象属性时,没有动态绑定机制,哪里声明,那里使用(当前类)
package poly_;
public class poly03 {
public static void main(String[] args) {
AA aa=new BB();//向上转型
System.out.println(aa.sum());//结果为20+10=30
//运行类型是BB,先去BB找方法,没找到,去父类AA找
//在AA里面找到sum(i+10),然后getl(),又回到子类BB找到i=20
/*如果
* class AA{
* public int sum(){
* return i+10;
* }
* }
* 运行最终结果为20,i在当前声明类中找
*/
}
}
class AA{
int i=10;
public int sum(){
return getl()+10;
}
public int sum1(){
return i+10;
}
public int getl(){
return i;
}
}
class BB extends AA{
int i=20;
/*public int sum(){
return i+20;
}*/
public int sum1(){
return i+10;
}
public int getl(){
return i;
}
}
多态应用
- 多态数组:
数组的定义类型为父类类型,里面保存的实际元素类型为子类类型
Person[] persons=new Person[3]
Person[0] = new Person("a",20);
Person[1] = new Student("c",30,100);
Person[2] = new Teacher("c",40,10000);
for(int i= 0;i < persons,length; i++){
//person[i]编译类型是 Person,运行类型是是根据实际情况有JVM来判断
persons[i].say();//多态绑定机制
//如何调用子类的私有方法
if(persons[i] instanceof Student){//判断person[i]的运行类型是不是Student
//((Student)persons[i]).study();一条语句实现
Student student=(Student) persons[i];//向下转型,和上面等效
student.study();
}
}
}
- 多态参数
方法定义的形参类型为父类类型,实参类型允许为子类类型
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理