Java轻松掌握面向对象的三大特性封装与继承和多态
作者:小学生!!
1.封装
1.介绍
封装是指把抽象出的属性和方法封装在一起,数据被保护在内部,程序的其他部分只能通过被授权的方法,才能对数据操作。
2.封装的理解和好处
- 隐藏实现的细节
- 可以对数据验证,保证安全合理
3.封装的实现步骤
- 将属性私有化(private)
- 提供一个公共的set方法,用于对属性判断并赋值
- 提供一个公共的get方法,用于获取属性的值
public class Student { private Double score; public void setScore(Double score) { //还可以在这里进行属性的判断 this.score = score; } public Double getScore() { return score; }
2.继承
1.介绍
继承可以解决代码的复用,当多个类具有相同的属性和方法时,我们可以从中抽象出父类,然后再父类中定义这些相同的属性和方法,这样所有的子类就不需要再定义这些相同的属性和方法,只需要使用extends关键字来继承父类即可。
2.继承的基本语法
public class Graduate extends Student{ }
- 子类会自动拥有父类定义的属性和方法
- 父类又叫基类,超类
- 子类又称派生类
3.继承的使用细节
子类继承了父类所有的属性和方法,但是私有属性和方法不能在子类直接访问,要通过公共的方法去访问。
package com.wang.extend; public class Student {//父类 public int age; public String name; private Double score; String hobby; public Double getScore() { return score; }
package com.wang.extend; public class Graduate extends Student {//子类 public void test() { //子类不能直接访问private属性,要调用公共方法得到 System.out.println("大学生" + name + age + hobby + getScore()); } }
子类必须调用父类构造器,完成父类的初始化,当创建子类对象时,不管使用子类的哪一个构造器,默认会先去调用父类的无参构造器,若父类没有无参构造器,则必须在子类构造器中使用super()指定使用父类的哪个构造器完成父类初始化,否则编译失败。
package com.wang.extend; public class Student {//父类 public int age; public String name; //public Student() { //} public Student(int age, String name) { this.age = age; this.name = name; }
package com.wang.extend; public class Graduate extends Student { public Graduate() { // super();//默认会有一个调用父类无参构造的方法super() //若父类没有无参构造器,必须用super()指定使用哪个构造器 super(20,"小明"); } }
super()在使用时,需要放在第一行。
super()和this()都只能放在构造器第一行,因此这两个方法不能同时存在于同一个构造器。
Java所有的类都是Object的子类。查看类的层级Ctrl+H。
父类构造器的调用不限于直接父类!将一直向上追溯到Object类(顶级父类)。
子类最多继承一个父类,java的单继承机制。
不能滥用继承,子类和父类要符合包含关系。比如,大学生包含在学生当中,这是学生就可以是父类,大学生为子类。
3.super关键字
1.基本介绍
super代表的是父类的引用,用于访问父类的属性,方法,构造器
2.基本语法
1.访问父类的属性/方法,但不能访问父类的private属性/方法
super.属性名; super.方法名(形参列表);
2.访问父类的构造器
super(形参列表);//只能放在构造器的第一句
3.细节与好处
1.父类属性由父类初始化,子类的属性子类初始化,分工明确。
2.当子类与父类的成员重名时,若要访问父类的成员,必须通过super。
3.super的访问不限于直接父类,当多个基类都有同名的成员,使用super访问时遵循就近原则。
4.super与this的比较
区别点 | this | super |
---|---|---|
访问属性 | 先访问本类的属性,若本类没有再去父类 | 只访问父类的属性 |
调用方法 | 先访问本类的方法,若本类没有再去父类 | 直接访问父类的方法 |
调用构造器 | 调用本类的构造器,必须放第一句 | 调用父类的构造器,必须放第一句 |
特殊 | 表示当前对象 | 表示子类访问父类对象 |
4.方法重写
1.基本介绍
子类的一个方法与父类的名称,返回类型,形参列表都一样,称子类是父类方法的重写。
2.注意事项与使用细节
1.子类方法的返回类型与父类方法的返回类型相同或者父类返回类型的子类。
//正确示例 public Object a() //父类返回类型为Object public String a() //子类返回类型为String或者Object //Object是String的父类
2.子类方法不能缩小父类方法的访问权限。
//接下来是错误示例 void display() //这是子类 子类访问权限为 默认的 public void display() //这是父类 父类访问权限为public //public->默认的 访问权限变小所以报错
3.重载与重写的比较
名称 | 发生范围 | 方法名 | 形参列表 | 返回类型 | 修饰符 |
---|---|---|---|---|---|
重载 | 本类 | 必须相同 | 参数类型,个数或者顺序中至少有一个不同 | 无要求 | 无要求 |
重写 | 父子类 | 必须相同 | 必须相同 | 子类的返回类型相同或者是父类的返回类型的子类 | 不能缩小父类的访问范围 |
3.多态
1.基本介绍
方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。
2.具体体现
1.方法的多态
重写和重载体现多态。
//重载 public int sum(int a,int b); public int sum(int a,int b,int c); //重写 class A{ public void say(){ System.out.printf("A是父类~");} } class B extends A{ public void say() { System.out.printf("B是子类~");} }
2.对象的多态(重点)
- 一个对象的编译类型和运行类型可以不一致。
- 编译类型在定义对象时,就确定了,不能改变,运行类型可以变化。
- 定义时 = 的左边是编译类型,右边是运行类型。
Animal animal = new Dog();//Animal是编译类型,Dog是运行类型 animal = new cat();//编译类型不能改变,运行类型可变
3.多态注意事项和细节讨论
1.多态的前提
两个对象(类)存在继承关系。
2.属性
没有重写,属性的值是看编译类型。
public class Tes { public static void main(String[] args) { A a = new B(); System.out.println(a.n); //这里输出的是10,因为属性的值要看编译类型,a的编译类型为A,所以输出的是A的属性 } } class A { public int n = 10; } class B extends A { public int n = 20; }
3.instance of
比较操作符,是用来判断对象的运行类型是否为某类型或者某类型的子类型。
4.多态的向上转型
- 本质:父类的引用指向了子类的对象。
- 语法:父类类型 引用名 = new 子类类型();
- 特点:可以调用父类中的所有成员(遵守访问权限),不能调用子类的特有成员,但最终的运行结果看运行类型的具体实现,能调用那些成员看编译类型,具体运行看运行类型。
5.多态的向下转型
- 语法:子类类型 引用名 = (子类类型) 父类引用;
- 只能强转父类的引用,不能强转父类的对象。
- 要求父类的引用必须指向的是当前目标类型的对象。
- 可以调用子类类型中的所有成员。
public class Test { public static void main(String[] args) { //向上转型 A a = new B(); a.say(); //a.abc(); 方法abc为运行类型B类特有的编译类型A类不能调用 //调用那些成员看编译类型,具体运行看运行类型 //向下转型 B b = (B) a; //父类的引用必须指向的是当前目标类型的对象 b.abc(); } } class A { public void say (){ System.out.println("A类的say()被调用"); } } class B extends A { public void abc(){ System.out.println("B类的abc()被调用"); } public void say (){ System.out.println("B类的say()被调用"); } }
运行结果如下:
6.Java动态绑定机制
当调用对象的方法时,该方法会与该对象的内存地址/运行类型绑定。
当调用对象的属性时,不存在动态绑定机制。
public class Test { public static void main(String[] args) { A a = new B();//A为编译类型,B为运行类型 System.out.println(a.sum()); //B类中sum()没注释前 结果为 40 注释后结果为 30 //注释后会调用A类的sum方法但因为动态绑定机制sum中的getI方法调用的是B中的 System.out.println(a.sum1()); //B类中sum()没注释前 结果为 30 注释后结果为 20 //调用A类的sum1,i由于是调用对象的属性,不存在动态绑定机制,哪里声明就调用哪的i=10 } } public class A { public int i = 10; public int sum() { return getI() + 10; } public int sum1() { return i + 10; } public int getI() { return i; } } public class B extends A { public int i = 20; // public int sum() { // return i + 20; // } // public int sum1() { // return i + 10; // } public int getI() { return i; } }
7.多态数组多态参数
多态数组
数组的定义类型为父类类型,里面保存的实际元素类型为子类类型和父类类型。
多态参数
方法定义的形参类型为父类类型,实际类型允许为子类类型。
示例:父类为Employee,子类为Worker和Manage
public class Test { public static void main(String[] args) { Test test = new Test(); Employee employee = new Employee("Bob",3000); Employee[] employee1 = new Employee[5];//多态数组 父类定义 Worker worker = new Worker("Tom",5000); Worker worker1 = new Worker("Kit",6000); Manager manager = new Manager("Smith",12000,100000); Manager manager1 = new Manager("Sab",15000,110000); employee1[0]=worker;//可放子类 employee1[1]=worker1; employee1[2]=manager; employee1[3]=manager1; employee1[4]=employee;//也可放本类 test.showEmpAnnal(worker);//多态参数 实参可为子类 test.showEmpAnnal(manager); test.testwork(employee1); } public void showEmpAnnal(Employee e){//多态参数 形参为父类 System.out.println(e.getName()+" 的年薪为:"+e.getAnnual()); } public void testwork(Employee e[]){ for (int i = 0; i <5 ; i++) { if(e[i] instanceof Worker){ ((Worker) e[i]).work();//向下转型 }else if(e[i] instanceof Manager){ ((Manager) e[i]).manage(); }else { System.out.println("员工 "+e[i].getName()+" 正在摸鱼!"); } } } }
到此这篇关于Java轻松掌握面向对象的三大特性封装与继承和多态的文章就介绍到这了,更多相关Java封装 继承 多态内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!