Java基础-封装、继承、多态

封装

1、概念

将类的某些信息隐藏在类的内部,不允许外部程序直接访问,而是通过该类提供的方法来对隐藏的信息进行操作和访问。

因此,封装就有两个含义:把该隐藏的隐藏起来,把该暴露的暴露出来。

2、好处

a) 封装可以隐藏实现的细节

b) 让使用者只能通过实现写好的访问方法来访问这些字段,这样一来我们只需要在这些方法中增加逻辑控制,限制对数据的不合理访问、

c) 方便数据检查,有利于于保护对象信息的完整性

d) 便于修改,提高代码的可维护性

3、封装的实现步骤

a) 修改属性的可见性设为(private)

b) 创建getter/setter方法(用于属性的读写)(通过这两种方法对数据进行获取和设定,对象通过调用这两种发方法实现对数据的读写)

c) 在getter/setter方法中加入属性控制语句(对属性值的合法性进行判断)

 1 public class Person{
 2     private String name;
 3     private int age;
 4  5     public int getAge(){
 6       return age;
 7     }
 8  9     public String getName(){
10       return name;
11     }
12 13     public void setAge(int age){
14       this.age = age;
15     }
16 17     public void setName(String name){
18       this.name = name;
19     }
20 }

 

 

继承

1、概念

继承是面向对象最显著的一个特性。继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。

继承可以使用 extends 和 implements 这两个关键字来实现继承,而且所有的类都是继承于 java.lang.Object,当一个类没有继承的两个关键字,则默认继承object(这个类在 java.lang 包中,所以不需要 import)祖先类。

在Java之中,如果要实现继承的关系,可以使用如下的语法:

  • class 子类 extends 父类 {}

子类又被称为派生类; 父类又被称为超类(Super Class)。

 1 package com.wz.extendsdemo;
 2 
 3 class Person {
 4     private String name;
 5     private int age;
 6 
 7     public void setName(String name) {
 8         this.name = name;
 9     }
10 
11     public void setAge(int age) {
12         this.age = age;
13     }
14 
15     public String getName() {
16         return this.name;
17     }
18 
19     public int getAge() {
20         return this.age;
21     }
22 }
23 
24 class Student extends Person { // Student类继承了Person类
25 }
26 
27 public class TestDemo {
28     public static void main(String args[]) {
29         Student stu = new Student(); // 实例化的是子类
30         stu.setName("张三"); // Person类定义
31         stu.setAge(20); // Person类定义
32         System.out.println("姓名:" + stu.getName() + ",年龄:" + stu.getAge());
33     }
34 }

通过代码可以发现,子类(Student)并没有定义任何的操作,而在主类中所使用的全部操作都是由Person类定义的,这证明:子类即使不扩充父类,也能维持父类的操作。

 1 package com.wz.extendsdemo;
 2 
 3 class Person {
 4     private String name;
 5     private int age;
 6 
 7     public void setName(String name) {
 8         this.name = name;
 9     }
10 
11     public void setAge(int age) {
12         this.age = age;
13     }
14 
15     public String getName() {
16         return this.name;
17     }
18 
19     public int getAge() {
20         return this.age;
21     }
22 }
23 
24 class Student extends Person { // Student类继承了Person类
25     private String school; // 子类的属性
26 
27     public void setSchool(String school) {
28         this.school = school;
29     }
30 
31     public String getSchool() {
32         return this.school;
33     }
34 }
35 
36 public class TestDemo {
37     public static void main(String args[]) {
38         Student stu = new Student(); // 实例化的是子类
39         stu.setName("张三"); // Person类定义
40         stu.setAge(20); // Person类定义
41         stu.setSchool("清华大学"); // Student类扩充方法
42         System.out.println("姓名:" + stu.getName() + ",年龄:" + stu.getAge() + ",学校:" + stu.getSchool());
43     }
44 }

以上的代码,子类对于父类的功能进行了扩充(扩充了一个属性和两个方法)。但是思考一下:子类从外表看是扩充了父类的功能,但是对于以上的代码,子类还有一个特点:子类实际上是将父类定义的更加的具体化的一种手段。父类表示的范围大,而子类表示的范围小。

2、继承的特性

a)子类拥有父类非 private 的属性、方法。

b) 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。

c) 子类可以用自己的方式实现父类的方法。 

d) Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 B 类继承 A 类,C 类继承 B 类,所以按照关系就是 B 类是 C 类的父类,A 类是 B 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。

e) 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。

3、继承类型

  • 单继承
  • 多重继承
  • 不同类继承同一个类
  • 多继承(不支持),但是可以通过implements实现多继承
 1 public interface A {
 2     public void eat();
 3     public void sleep();
 4 }
 5  
 6 public interface B {
 7     public void show();
 8 }
 9  
10 public class C implements A,B {
11 }

4、super 与 this 关键字

super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。

this关键字:指向自己的引用。

 1 class Animal {
 2   void eat() {
 3     System.out.println("animal : eat");
 4   }
 5 }
 6  
 7 class Dog extends Animal {
 8   void eat() {
 9     System.out.println("dog : eat");
10   }
11   void eatTest() {
12     this.eat();   // this 调用自己的方法
13     super.eat();  // super 调用父类方法
14   }
15 }
16  
17 public class Test {
18   public static void main(String[] args) {
19     Animal a = new Animal();
20     a.eat();
21     Dog d = new Dog();
22     d.eatTest();
23   }
24 }

 

 

多态

1、概念

a) 多态是同一个行为具有多个不同表现形式或形态的能力。同一个接口,使用不同的实例而执行不同操作。

b) 多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。

c) java 的这种机制遵循一个原则:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。 
      1. 如果a是类A的一个引用,那么,a可以指向类A的一个实例,或者说指向类A的一个子类。
      2. 如果a是接口A的一个引用,那么,a必须指向实现了接口A的一个类的实例。

2、多态存在的三个必要条件

  • 继承
  • 重写
  • 父类引用指向子类对象:Parent p = new Child();

3、多态的实现方式

方式一:重写:

重写

方式二:接口

  • 1. 生活中的接口最具代表性的就是插座,例如一个三接头的插头都能接在三孔插座中,因为这个是每个国家都有各自规定的接口规则,有可能到国外就不行,那是因为国外自己定义的接口类型。

  • 2. java中的接口类似于生活中的接口,就是一些方法特征的集合,但没有方法的实现。

方式三:抽象类和抽象方法

TODO

4、Java多态性实现机制

SUN目前的JVM实现机制,类实例的引用就是指向一个句柄(handle)的指针,这个句柄是一对指针:

一个指针指向一张表格,实际上这个表格也有两个指针(一个指针指向一个包含了对象的方法表,另外一个指向类对象,表明该对象所属的类型);

另一个指针指向一块从java堆中为分配出来内存空间。

5、总结

a) 通过将子类对象引用赋值给超类对象引用变量来实现动态方法调用。

1 DerivedC c2=new DerivedC();
2 BaseClass a1= c2;            //BaseClass 基类,DerivedC是继承自BaseClass的子类
3 a1.play();                   //play()在BaseClass,DerivedC中均有定义,即子类覆写了该方法

分析:

* 为什么子类的类型的对象实例可以覆给超类引用?
  自动实现向上转型。通过该语句,编译器自动将子类实例向上移动,成为通用类型BaseClass;
* a.play()将执行子类还是父类定义的方法?
  子类的。在运行时期,将根据a这个对象引用实际的类型来获取对应的方法。所以才有多态性。一个基类的对象引用,被赋予不同的子类对象引用,执行该方法时,将表现出不同的行为。
  在a1=c2的时候,仍然是存在两个句柄,a1和c2,但是a1和c2拥有同一块数据内存块和不同的函数表。

b)不能把父类对象引用赋给子类对象引用变量
1 BaseClass a2=new BaseClass();
2 DerivedC c1=a2;//出错
在java里面,向上转型是自动进行的,但是向下转型却不是,需要我们自己定义强制进行。
1 c1=(DerivedC)a2;
进行强制转化,也就是向下转型。
c) 记住一个很简单又很复杂的规则,一个类型引用只能引用引用类型自身含有的方法和变量。
你可能说这个规则不对的,因为父类引用指向子类对象的时候,最后执行的是子类的方法的。
其实这并不矛盾,那是因为采用了后期绑定,动态运行的时候又根据型别去调用了子类的方法。而假若子类的这个方法在父类中并没有定义,则会出错。)
分析:
当你使用父类引用指向子类的时候,其实jvm已经使用了编译器产生的类型信息调整转换了。
这里你可以这样理解,相当于把不是父类中含有的函数从虚拟函数表中设置为不可见的。注意有可能虚拟函数表中有些函数地址由于在子类中已经被改写了,所以对象虚拟函数表中虚拟函数项目地址已经被设置为子类中完成的方法体的地址了。

6、Java与C++多态性的比较

jvm关于多态性支持解决方法是和c++中几乎一样的,
只是c++中编译器很多是把类型信息和虚拟函数信息都放在一个虚拟函数表中,但是利用某种技术来区别。
 
Java把类型信息和函数信息分开放。Java中在继承以后,子类会重新设置自己的虚拟函数表,这个虚拟函数表中的项目有由两部分组成。从父类继承的虚拟函数和子类自己的虚拟函数。
虚拟函数调用是经过虚拟函数表间接调用的,所以才得以实现多态的。

Java的所有函数,除了被声明为final的,都是用后期绑定。
C++实现多态性,使用关键字virtual,为了引起晚捆绑,使用虚函数。若一个函数在基类被声明为virtual,则所有子类中都是virtual的。对虚函数的重定义成为越位。
 

a) 虚函数

虚函数的存在是为了多态。

Java 中其实没有虚函数的概念,它的普通函数就相当于 C++ 的虚函数,动态绑定是Java的默认行为。如果 Java 中不希望某个函数具有虚函数特性,可以加上 final 关键字变成非虚函数。

 1 public class Override {
 2     public static void main(String[] args) {
 3         People p=new Stu();
 4         p.eat();
 5         //调用特有的方法
 6         Stu s=(Stu)p;
 7         s.study();
 8     }
 9 }
10 class People{
11     public void eat(){
12         System.out.println("吃饭");
13     }
14 }
15 class Stu extends People{
16     @java.lang.Override
17     public void eat(){
18         System.out.println("学生吃");
19     }
20     public void study(){
21         System.out.println("好好学习");
22     }
23 }
24 class Teachers extends People{
25     @java.lang.Override
26     public void eat(){
27         System.out.println("老师吃");
28     }
29     public void teach(){
30         System.out.println("认真授课");
31     }
32 }

 

 

posted @ 2021-09-26 21:53  r1-12king  阅读(82)  评论(0编辑  收藏  举报