Java面向对象

一.什么是面向对象,什么又是面向过程?

对比面向过程,是有两种不同的处理问题的角度的

面向过程更注重处理事情的每一个步骤及顺序,面向对象更注重事情有哪些参与者(对象),及各自需要做什么?

比如:洗衣机洗衣服

面向过程:会将任务拆解成一系列的步骤(函数)/(方法),

1. 打开洗衣机----->   2.洗衣服------>  3.放洗衣粉------>  4.清洗------>  5.烘干

面向对象:会拆出人和洗衣机两个对象: 

人:打开洗衣机  放衣服 放洗衣粉(人可以称为类)(打开洗衣机 放衣服 放洗衣粉就是类中的方法)

洗衣机: 清洗 烘干(只需要把衣服为参数传给洗衣机即可)

从以上例子可以看出:面向过程比较高效,而面向对象更易于复用,扩展和维护

二.面向对象的三大特征

1.封装

1.封装的好处

1.,在于明确标识出允许外部使用的所有成员方法,可以在不影响使用的情况下改变类的内部结构,同时保护了数据

2.内部细节对外部调用透明,外部调用无需修改或者关心内部实现

3.对于外界它的内部细节是隐藏的,暴露给外界的只能是它的访问方法
 

2.属性的封装

使用者只能通过事先定制好的方法来访问数据,可以方便地加入逻辑控制,限制对属性的,不合理操作

javabean的属性私有,提供get,set方法对外访问(set赋值)(get获取),因为属性的赋值或者获取逻辑只能由javabean本身决定赋值规则,而不能由外随意修改 

//创建一个女朋友
public class GirlFriend {
    private int age;
    
    public void setAge(int age) {
        if (18 < age && age < 40) {
            this.age = age;
        }else {
            System.out.println("我并没有这个岁数的女朋友,你认错了");
        }
    }
}

比如上例:哥们你也不想你的女朋友是个60岁的吧,所以就要用封装,不让别人乱给你定女朋友年龄

3.方法的封装

使用者按照既定的方式调用方法,不必关心方法内部的实现,便于使用;便于修改,增强代码的可维护性;

private Node findNode(int index){
    int i = 0;
    for (Node p = head; p != null ;p = p.next , i++){
        if (index == i){
            System.out.println("index的索引为" + i);
        return p;
        }
    }
    return null;//没找到
}
//get方法
public int get(int index){
    //node是指定索引的节点
    Node node = findNode(index);
    if (node == null){
        //抛出异常
        //String.format表示字符串中有一些东西是可变的
        throw new IllegalArgumentException(String.format("index[%d] 不合法%n",index));
    }
    return node.value;
}

比如上例:别人调用只需要通过get传入index(索引)获得链表的值,并不需要知道你通过findNode()怎么获得该索引的链表对象 

 2.继承

 1.继承的好处

1.继承基类(父类)的方法,并做出自己的改变和扩展

2.子类共性的方法或者属性直接使用父类的,而不需要自己再定义,只需要扩展自己个性化的

进行一下简单的介绍:继承是从已有的类.新的类能吸收已有类的数据属性和行为,并能扩展新的能力.在本质是上特殊~一般的关系.

比如:猫类、狗类、虎类中可以抽象出一个动物类,具有和猫、狗、虎、类的共同特征(吃、跑、叫等)。

2.什么不能被继承

通过extends关键字来实现继承,private和static定义的变量和方法不可以可以被继承。

final修饰的类也不可以被继承,子类不能继承父类的构造方法。

3.super关键字

1.虽然子类不能继承父类的构造方法,但是需要先帮父类完成构造,才能构造自己!!

子类对象的实例化之前,必须在子类的构造方法中,调用父类的构造方法

1.1通过super(...)调用

1.2当父类有无参构造方法时,super0可以省略

Java面向对象_第1张图片

2. 通过super可以明确访问父类中的属性和方法(有访问权限的前提下)

public class Dog extends Animal{
    
    public void show (){
        //调用父类的成员方法
        super.eat();
        //调用父类的成员属性
        String dogName = super.name;
    }
}

比如,上例的狗类可以通过super关键字来调用父类的成员属性(name) 成员方法(eat) 

总结super关键字:

就是你在子类中仍需要用到父类的东西时候,就需要用super关键字

而super调用构造方法是必然的,因为你在给子类构造实例的时候必须通过super先给父类实例  

4.this关键字

 居然说了super关键字,那么this关键字肯定是必不可少的

1.区分局部变量和成员变量

public class Actor {
    private String name;
    private int age;

    public Actor(String name, int age) {
        name = name;
        age = age;
    }

}

此时你的name = name :虚拟机就会使用就近原则进行赋值 

让你的name自己给自己赋值(局部变量给局部变量赋值)而你在调用name的时候就是null

所以为了解决这个问题:此时我们就需要引用this 关键字

public class Actor {
    private String name;
    private int age;

    public Actor(String name, int age) {
        this.name = name;
        this.age = age;
    }

}

此时this.name就表示成员变量(定义在类中的变量),而后面的name通过就近原则就是局部变量

 (定义在方法中的变量)

2.this是一个引用

 this表示方法调用者的地址值

public class Actor {
    private String name;
    private int age;

    public Actor(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void show(){
        System.out.println(this.name);
    }
public class Test {
    public static void main(String[] args) {
        Actor a1 = new Actor("张三",19);
        Actor a2 = new Actor("李四",19);
        a1.show();
        a2.show();
    }
}

 此时如果用a1调用show()方法,this引用的地址就是a1所以说show方法中的this.name就等价于a1.name

补充知识点:Object 是所有类的祖先类(没有任何属性)

如果一个类没有出现extends关键字,则这个类会默认继承Object。

因为所有类都直接或者间接继承于Object,因为基本你存在extends但是你迟早有一个父类会没有extends关键字的。

父类和子类的关系相当于“是一个(is a)”的关系,即子类是父类的一个对象,而不是“有(has)”的关系,而这个has关系就是我们下面学的接口的实现了。

3.多态

对于多态的个人见解:我认为相比于封装和继承,多态是三大特征中比较难的一个,封装和继承最终归结于多态,多态是指的类和类的关系,两个类由继承关系,存在方法重写,故而可以调用时有父类引用指向子类对象,多态必备的三个要素:继承,重写,父类引用指向子类对象 

1.多态的好处

1.基于对象所属类的不同,调用同一个重写的方法,表现的行为不一样,这种思想,叫做多态!!

比如:猫和狗都调用了say()(叫)方法,狗是汪汪汪,猫是喵喵喵

2.代码更加灵活:无论右边new的时候换成哪个子类对象,等号左边调用的方法都不会变化

因为调用方法:编译看左边,运行看右边(我在下面将会讲解)

3.提高程序的扩展性:定义方法的时候,使用父类类型作为参数,将来使用时,使用具体的子类类型操作 

public class Target {
    public static void main(String[] args) {
        Cat c = new Cat();
        Dog d = new Dog();
        Elephant e = new Elephant();
        eat(c);
    }
    public static void eat(Animal animal){
        animal.eat();
    }
}

上例:你不想让猫吃了 ,只需要在eat()方法里面换一个参数即可

4..能够降低代码的“圈复杂度”避免使用大量的 if else代码

public static void drawMaps1() {

    Cycle cycle = new Cycle();

    Rect rect = new Rect();

    Flower flower = new Flower();

    String[] strings = {"cycle", "rect", "cycle", "rect", "flower"};

    for (String s : strings) {

        if (s.equals("cycle")) {

            cycle.draw();

        } else if (s.equals("rect")) {

            rect.draw();

        } else {

            flower.draw();

        }

    }

}

这样就会有大量的if else 而我们应该怎么优化呢此时就用到了我们的多态

public static void drawMaps() {

    Cycle cycle = new Cycle();

    Rect rect = new Rect();

    Flower flower = new Flower();

    Triangle triangle = new Triangle();

    Shape[] shapes = {cycle,rect,cycle,rect,flower,triangle};//发生了向上转型

    for(Shape shape : shapes) {

        shape.draw();

    }

}

这样优化是不是简便了许多 

2.重写

 1.重载和重写不同

重写:方法名,参数列表,返回类型(除过子类中方法的返回值是父类中方法返回值的子类时)都相同的情况下, 对方法体进行修改或重写

重载:同名的方法如果有不同的参数列表(参数类型不同、参数个数不同甚至是参数顺序不同)则视为重载。同时,重载对返回类型没有要求,可以相同也可以不同,但不能通过返回类型是否相同来判断重载

Java面向对象_第2张图片

 重写就相当于图一,俩个箭绑一起将后面的箭进行覆盖

重载就相当于图二,每个箭都是独立的个体都有独自的作用

2.重写的意义

设置子类重写的意义,一般都是父类方法满足不了自己了,自己想要进行扩充

Java面向对象_第3张图片

比如这个打电话,若干年后,需要增加新的功能(显示地区,显示姓名),就需要重写来电显示这个方法进行重写(覆盖),而如果原有的显示号码你扔需要使用就只需要通过super.来电显示方法即可,使用父类中的方法。

3.重写的前提条件(什么东西可以重写) 

(1).被private修饰的方法 不能进行重写

(2).被static修饰的方法 不能进行重写 

(3).被final修饰的方法不能进行重写,此时这个方法,被叫做密封方法

(4).访问修饰限定符 private < 默认权限 < protected < public

(5).方法的返回值可以不同,但是必须是父子类关系 

(6).构造方法 不能发生重写

3.向上转型

有三种方式可以发生向上转型

1.直接赋值

//父类引用 引用了子类对象
Animal a1 = new Cat();
Animal a2 = new Dog();
Animal a3 = new Elephant();

2.方法传参的方式.

public class Target {
    public static void main(String[] args) {
       
        Cat cat = new Cat();
        eat(cat);
    }
    public static void eat(Animal animal){

        animal.eat();
    }

}

3.返回值

public static Animal func(){

    return new Dog();
}

3.多态的弊端(会涉及向下转型和instanceof关键字)

 1.属性没有多态性(包含俩个顺口溜)

当父类和子类都有同名属性的时候,通过父类引用,只能引用父类自己的成员属性 

这也就是那个顺口溜,调用成员变量编译看左边运行看左边,

这句话的意思就是,代码报不报错就看左边有没有这个成员方法,运行的结果也是看左边的成员方法的值 

比如:

Animal a2 = new Dog();

System.out.println(a2.name);

编译通不通过就看Animal类中有没有name这个变量有就不报错,没有就报错,而运行的结果还是看Animal的name值

此外还有有另一个顺口溜就是,调用成员方法,编译看左边运行看右边。

 这句话的意思就是,代码报不报错就看右边有没有这个成员方法,运行的结果就看右边的成员方法的值-------> 可以这样理解吧:看子类有没有重写父类的这个方法进行覆盖,如果没有覆盖就是父类的成员方法 。

public class Test {
    public static void main(String[] args) {
        Person a1 = new Student("张三",19);
        a1.show();
    }
}

编译通不通过就看Person类中有没有show这个方法有就不报错,没有就报错,而运行的结果还是看Student的show()方法,如果Student没有show()方法,就看Person的show()方法

2.构造方法没有多态性

class B {
        public B() {
            // do nothing
            func(); // 会调用子类的func
        }
        public void func() {
            System.out.println("B.func()");
        }
    }

    class D extends B {

        private int num = 1;

        D() {
            super();
        }
        @Override
        public void func() {
            //0
            System.out.println("D.func() " + num);
        }
    }
    public class Test2 {
        public static void main(String[] args) {
            D d = new D();
            
        }
    }

比如这道题答案是什么呢?如果你的答案对了你就理解了构造方法没有多态性了

答案:是输出的是D.func()0 你对了吗?

思路分析:为什么输出0?

是因为父类还没有走完子类并不会赋值

super()走子类的构造方法,因为调用方法时候还父类的构造方法还没有没有走完,所以并不会让

成员变量赋值成功!!

但是这样的代码不建议写,尤其是在父类构造方法中调用子类方法

构造子类的同时,会调用子类的构造方法

所以如果属性也具有都多态,num的值就会是1

3.不能调用子类特有的方法

个人见解:我认为子类最大的弊端就是不能调用子类特有的方法

Java面向对象_第4张图片

 此时调用Bird类特有的fly方法就会报错,让你在Animal类中创建fly方法

解决方案:就是通过向下转型将其转换为Bird

public class Target {
    public static void main(String[] args) {
        Animal a1 = new Dog();
        Animal a2 = new Bird();
        Bird b = (Bird) a2;
        b.fly();
    }
}

通过向下转型即可解决上述问题,但是又会出现新的问题,向下转型并不安全。为什么呢?请跟我看下例

public class Target {
    public static void main(String[] args) {
        Animal a1 = new Dog();
        Animal a2 = new Bird();
        Bird b = (Bird) a1;
        b.fly();
    }
}

狗会飞吗?通过上述代码肯定会,但是代码会有bug,这就是不安全的原因

此时解决这个新的问题,就会涉及到一个关键字instanceof

public class Target {
    public static void main(String[] args) {
        Animal a1 = new Dog();
        Animal a2 = new Bird();
        if (a1 instanceof Bird) {
            Bird bird = (Bird) a1;
            bird.fly();
        } else {
            System.out.println("不能飞");
        }
    }

此时就会通过 if (a1 instanceof Bird) 表示判断a1对象是不是表示为Bird类,是进入代码进行强转,不是就不会进入。

三.总结

        面向对象编程是利用类和对象编程的一种思想。

正所谓万物可归类是对于世界事物的高度抽象,不同的事物之间有不同的关系。

一个类自身与外界的封装关系,一个父类和子类的继承关系,一个类和多个类的多态关系。

正所谓万物皆对象,对象是具体的世界事物。

面向对象的三大特征封装,继承,多态。

封装,封装说明一个类行为和属性与其他类的关系,低耦合,高内聚;

继承是父类和子类的关系。

多态说的是类与类的关系。 

欢迎大佬们职责我的错误,我一定会积极听取并改正错误的,最后点个赞吧!谢谢大家!祝大家周末愉快! 

你可能感兴趣的:(java,开发语言)