Java基础之面向对象(封装、继承、多态)

时间:2021-10-02 21:54:50

一、面向对象的概念

        面向对象是程序设计的一种思想,是从面向过程编程演变而来的。举一个例子,我们如何把大象装冰箱里,按照面向过程的思想来回答这个问题就是:1、把冰箱门打开;2、把大象放冰箱里;3、把冰箱门关上;如果按照面向对象的思想来思考这个问题,我们看到的就是冰箱这一个对象,它具有打开动作、存储动作、关闭动作,就是将这些功能封装到冰箱这个对象中,只要我们找到冰箱这个对象,就可以执行它的打开、存储、关闭等操作。再比如:你要买电脑,但是你不懂,但是你可以找一个懂电脑的人来帮你买电脑,这个人就是所谓的对象。懂电脑的人代表的是一类人,这里称之为类,而具体帮你买电脑的可能是张三,李四等某一个人,而这某一个具体的人就被称之为对象。这也就是类与对象的关系。我们在编程中需要做的事情就是找到可以实现某些功能的对象来帮助我们完成某些特定的功能。而类就是JAVA语言中描述现实生活的事物,对象就是现实生活中确实存在的那个事物。

        面向对象有三大特性,封装、继承和多态,下面我们一一学习

二、面向对象之封装

1、封装的概念:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。比如说:我们的电脑的CPU,内存,显卡,电源等装备我们是看不到的,因为他们都被封装在机箱里面了,而我们只需要按一下电脑为我们提供的电源按钮就可以启动电脑为我们工作,这样大大的提高了设备的安全性以及使用的方便性。另外,函数就是java中最小的封装体
2、封装的好处:
        1、将变化隔离。对象的内部变化只在对象内部体现,我们在外部看不到
        2、便于使用。我们无需知道对象内部的构造,直接拿过来通过它对外提供的访问方式来使用
        3、提高重用性。比如我们的机箱里面的部件可以随便换,但是这个机箱还是这个机箱
        4、提高安全性。不能直接使用对象的成员,通过公共方法访问,可以在对象内部采取安全措施,避免外部错误操作的发生
3、封装的原则:
        1、将不需要对外提供的内容都隐藏起来。
        2、把属性都隐藏,提供公共方法对其访问。

4、private关键字:

       封装使用的关键字是private。用于修饰类中的成员(成员变量和成员方法),只能在本类中访问,private是java语言中最低权限。私有是封装一种体现形式。访问类中被private修饰的成员变量的公共访问方法,赋值函数定义为set,取值函数定义为get。定义在类中的成员变量,一般都会加上private。代码如下:

private String name;
private int 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;
}

5、this关键字:
        this代表所在函数所属对象的引用,哪个对象调用this所在的函数,this就代表哪个对象
this的应用:
        1、当定义类中功能时,该函数的内部要调用到该函数的对象时,用this来表示这个对象。但凡本类内部功能中使用到了本类对象,都用this表示
        2、用于构造函数之间相互调用
        3、this关键字只能定义在构造函数的第一行

6、static关键字

        用法:是一个修饰符,用于修饰成员(成员变量,成员函数),不能存在于方法中
        当成员被静态修饰后,就多了一种调用方式除了可以被对象调用外,还可以直接用类名调用。类名.静态成员

        特点:1、随着类的加载而加载;也就是说静态会随着类的消失而消失,说明其生命周期最长
                   2、优先于对象存在
                   3、被所有对象共享
                   4、可以直接被类名所调用
       类变量和实例变量的区别:
       1、存放位置
            类变量随着类的加载存在于方法区中
            实例变量随着对象的建立而存在于堆内存中
       2、声明周期
            类变量声明周期最长,随着类的消失而消失
            实例变量的声明周期随着对象的消失而消失
       3、主函数是静态的
      注意事项:
       1、静态方法只能访问静态成员
       2、非静态方法既可以访问非静态成员,也可以访问静态成员
       3、静态方法中不可以定义this、super关键字 因为静态优先于对象 所以静态方法中不可以出现this
      静态有利有弊:
      利:1、 对对象的共享数据进行单独空间存储,节省空间,
             2、可以直接被类名调用
      弊:1、生命周期过长
             2、访问具有局限性(静态只能访问静态)

      静态代码块  static{}:随着类的加载而加载;构造代码块{}:随着对象的初始化而加载,用于初始化对象,存放不同对象共性的内容

7、封装的应用--单例设计模式

        单例设计模式的出现是为了保证对象在内存中的唯一性。建立对象的时候,绝不可以去 new创建对象。因此我们需要在类的内部创建自己的对象,然后提供一个公共方法让外界可以获取此对象,无论有多少次使用,该对象只创建了一次,因此称为单例设计模式。单例设计模式分为饿汉式和懒汉式:

        1、饿汉式:直接创建好自己的对象

class Single {
private Single() {
}

private static Single single = new Single();

public static Single getInstence() {
return single;
}
}
        2、懒汉式:当外界使用的时候才创建对象(存在安全隐患,开发不建议使用,但面试会问)
class Student {private Student() {}private static Student student = null;public static Student getStudent() {if (student == null) {synchronized (Student.class) {if (student == null) {student= new Student();}}}return student;}}

饿汉式与懒汉式有什么不同:

懒汉式特点在于实例的延迟加载,延迟加载在多线程访问时会出现安全问题,可以通过加同步来解决。用同步代码块和同步函数都可以,但是稍微有些低效。用双重判断的方式可以解决这个效率问题,加同步的时候使用的锁是该类所属的字节码文件对象

8、对象初始化步骤:

        Person per=new Person("lisi",20);这句话都做的什么?
        1、因为new用到了Person.class,所以会先找到Person.class文件并加载到内存中
        2、执行该类中的static代码块(如果有的话),给Person.class类进行初始化
        3、在堆内存中开辟空间,分配内存地址
        4、在堆内存中建立对象的特有属性,并进行默认初始化
        5、对属性进行显示初始化
        6、对对象进行构造代码块{}初始化
        7、对对象进行对应的构造函数初始化
        8、将内存地址赋给栈内存中的per变量

三、面向对象之继承

        概念:多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继那个类即可。多个类可以称为子类,单独这个类称为父类或者超类。子类可以直接访问父类中的非私有的属性和行为。通过 extends 关键字让类与类之间产生继承关系。如:

class SubDemo extends Demo{}

继承的出现提高了代码的复用性。继承的出现让类与类之间产生了关系,提供了多态的前提。

1、Java只支持单继承,不支持多继承。
        一个类只能有一个父类,不可以有多个父类。
        class SubDemo extends Demo{} //ok
        class SubDemo extends Demo1,Demo2...//error
2、Java支持多层继承(继承体系)
        class A{}
        class B extends A{}
        class C extends B{}
3、定义继承需要注意:
        不要仅为了获取其他类中某个功能而去继承;类与类之间要有所属( " is a " )关系,xx1是xx2的一种。

4、this和super的使用

        super和this的用法相像,this代表本类对象的引用,super代表父类的内存空间的标识。当子父类出现同名成员时,可以用super进行区分;子类要调用父类构造函数时,可以使用super语句。子类中有一行隐式语句super() 会访问父类中的空参数构造函数,子类中所有构造函数第一行都有super(),子类初始化时首先要访问父类的构造函数 如果要访问非默认的构造函数 则通过super(。。。)访问

        结论:子类中所有的构造函数都会访问父类默认的构造函数 当父类中没有空参数的构造函数时候 必须通过super指定访问父类中的构造函数,也可以使用this()语句访问本类中的构造函数 子类中至少有一个构造函数访问父类的构造函数 

        注意:this 和 super不能同时存在 因为都得写第一行进行初始化

5、函数覆盖(Override)

        1>子类中出现与父类一模一样的方法时,会出现覆盖操作,也称为重写或者复写。
        2>父类中的私有方法不可以被覆盖。
        3>在子类覆盖方法中,继续使用被覆盖的方法可以通过super.函数名获取。
        4>覆盖注意事项:
             覆盖时,子类方法权限一定要大于等于父类方法权限
             静态只能覆盖静态。
        5>覆盖的应用:
        当子类需要父类的功能,而功能主体子类有自己特有内容时,可以复写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容。

6、 final关键字

        1>final可以修饰类,方法,变量。
        2>final修饰的类不可以被继承。
        3>final修饰的方法不可以被覆盖。
        4>final修饰的变量是一个常量。只能被赋值一次。
        5>内部类只能访问被final修饰的局部变量。

以下是继承的使用示例代码:

class Fu{
Fu() {
System.out.println("fu");
}
int num=4;
void show(){
System.out.println("dddd");
}
}
class Zi extends Fu{
Zi() {
//super();子类初始化的时候会自动调用父类的构造方法
System.out.println("zi");
}
int num=5;
void show(){
super.show();//调用父类的show方法
System.out.println(num+"..."+super.num);
}
void print(){
System.out.println("dd");
}
}
public class ExtendsDemo {
public static void main(String[] args) {
Zi z=new Zi();
z.show();
}
}


四、面向对象之多态

概念:事物存在的多种形态
        1、多态的体现
             父类的引用指向自己子类对象
             父类的引用可以接受子类对象
        2、多态的前提
             必须是类与类之间有关系,要么继承要么实现
             存在覆盖
        3、多态的好处
             大大提高程序的扩展性
        4、多态的弊端:
             提高了扩展性,但是只能使用父类的引用访问父类的成员
        6、代码中的特点
             在多态中非静态成员函数的特点:
                 在编译时期:参阅引用型变量所属的类中是否有相应方法,如果有,编译通过,否则失败
                 在运行时期:参阅对象所属的类中是否有调用的方法;
             简单总结:成员函数在多态调用时 编译看左边 ,运行看右边
             多态中成员变量和静态函数 :无论编译和运行都参考左边,引用型变量所数的类

代码示例:
abstract class Animal{
abstract void eat();
}
class Cat extends Animal{
@Override
void eat() {
System.out.println("吃鱼");
}
public void catchMouse(){
System.out.println("抓老鼠");
}
}
class Dog extends Animal{
@Override
void eat() {
System.out.println("吃骨头");
}
}
public class DuoTaiDemo {
public static void main(String[] args) {
Animal a=new Cat();
Cat b=(Cat)a;//强制将父类的引用,转成子类类型,向下转型
//千万不要将父类对象转换成子类类型
eat(a);
b.catchMouse();
}
public static void eat(Animal a){
a.eat();
if(a instanceof Cat){
Cat c=(Cat)a;
c.catchMouse();
}
}
}