天天看点

Java程序员养成之路-基础知识4

面向对象

1.面向对象概述

面向对象是相对于面向过程而言的,所以,理解面向对象,先要理解面向过程是什么。

面向过程(Procedure Oriented)就是通过分析要解决的问题,并拆分成若干个步骤,然后按照这些步骤依次进行,比如做一道菜,那就需要依次进行买菜、洗菜、切菜、炒菜、装盘这些步骤,这就是面向过程的思想。

面向对象(Object Oriented,OO)则是把构成问题的事务按照一定规则划分为多个独立的对象,建立对象的目的不是为了完成一个步骤,而是为了描述某个事物在整个解决问题的步骤中的行为。

面向对象编程(Procedure Oriented Programming,OOP)的本质:以类的方式组织代码,以对象的组织封装数据。

2.面向对象特性

2.1 封装

(1)封装是面向对象的核心思想,将对象的属性和行为封装起来,不需要让外界知道具体实现细节。比如玩手机,我们并不需要知道手机内部到底是怎么运作的,就可以使用手机。

(2)程序的设计要追求“高内聚,低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合就是仅暴露少量的方法给外部使用。

(3)封装的优点:

  • 良好的封装可以降低耦合度。
  • 保护数据,可以对成员变量进行更精确的控制。
  • 隐藏代码,实现细节。
  • 增强系统的可维护性。
public class Student {
    // 使用private对变量进行修饰,达到封装的目的
    // 属性私有
    private String name;    // 姓名
    private int age;    // 年龄
    private char sex;   // 性别

    // 使用public对方法进行修饰,以便外部能够通过使用get、set方法对属性进行操作
    // 方法公有
    public String getName(){
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    // 对setAge()方法添加判断条件,外部看不到
    public void setAge(int age) {
        if (age < 120 && age > 0) {
            this.age = age;
        }else {
            age = 0;
        }
    }

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }
}
           
2.2 继承

(1)继承主要描述的是类与类之间的关系,继承可以在无需重新编写原有类的情况下,对原有类的功能进行扩展。

(2)继承的关键字是extends。Java中类只有单继承,没有多继承,即一个子类(派生类)有且仅能继承一个父类(基类、超类)。

/*动物类(父类)*/
public class Animal {
    private String name;
    private double weight;
    public void eat() {
        System.out.println("吃饭ing");
    }
}
           
/*狗类(子类)*/
public class Dog extends Animal {
    public void run() {
        System.out.println("跑步ing");
    }
}
           
/*测试类*/
public class Test {
    public static void main(String[] args) {
        // 创建一个对象
        Dog dog = new Dog();
        // 调用动物类的eat()方法
        dog.eat();
        // 调用狗类的run()方法
        dog.run();
    }
}
/*
	输出结果:
	吃饭ing
	跑步ing
*/

           
2.3 多态

(1)多态指的是在一个类中定义的属性和方法被其他类继承后,当把子类对象直接赋值给父类引用变量时,相同引用类型的变量调用同一个方法所呈现出的多种不同行为特性。

(2)多态的三个前提

  • 存在继承或实现关系
  • 子类重写父类方法
  • 父类引用指向子类对象(如Father father = new Son();)

(3)好处和弊端

  • 好处:提高程序的扩展性。定义方法时,使用父类型作为参数,以后使用的时候,就可以使用不同具体的子类型参与操作。
  • 弊端:不能使用子类特有方法。
/*动物类*/
public class Animal {
    public int age = 2;
    public void eat() {
        System.out.println("吃东西");
    }
}
           
// 子类(狗类)继承父类(动物类)
public class Dog extends Animal {
    public int age = 3;
    public int weight = 20;
    // 重写父类(动物类)方法
    @Override
    public void eat() {
        System.out.println("狗吃狗粮");
    }
    // 子类特有方法
    public void lookHouse() {
        System.out.println("狗狗看家");
    }
}
           
/*测试类*/
public class Test {
    public static void main(String[] args) {
        /*
          向上转型:
          从子类到父类,父类引用指向子类对象
        */
        Animal dog = new Dog();
        // 多态是方法的多态,属性没有多态性,因此下面输出为:2
        System.out.println(dog.age);
        // 子类重写父类方法,执行子类方法,因此下面输出为:狗吃狗粮
        dog.eat();
        // 不能调用子类特有的方法
        dog.lookHouse();    // 报错:Cannot resolve method 'lookHouse' in 'Animal'
        /*
            向下转型:
            从父类到子类,父类引用转为子类对象
        */
        ((Dog)dog).lookHouse();
    }
/*
    运行结果:
    2
    狗吃狗粮
    狗狗看家
*/
           

编译看左边,执行看右边:

父类变量引用指向子类对象时,该变量的成员变量和静态方法和父类保持一致,而对于非静态方法,编译时与父类一致,运行时则与子类一致。

3.类与对象的关系

类是对某一类事物的抽象描述,包括属性(成员变量)和行为(成员方法)。比如人,有身高、体重等属性,也有吃、喝、玩等行为,将这些共同的属性和行为提取出来就可以形成一个类,所以一个类可以简单理解为就是一个模板。

对象用于表示现实中该类事物的个体,是类的实例化、具体化,并且一个类可以对应多个对象。比如猫猫、狗狗、鸡鸡、鸭鸭等就是动物类的对象。当然,猫又分为黑猫、白猫、大猫、小猫,所以,猫也可以单独作为一个类。

类是抽象的,对象是具体的
public class Demo {
    public static void main(String[] args) {
        // 通过new创建一个对象
        Animal animal = new Animal();
        // 给该对象赋值
        animal.name = "小一";
        animal.weight = 20.20;
        // 调用该对象的方法
        animal.eat();   // 小一正在吃饭!现在是20.2kg。
    }
}
// 定义一个动物类
class Animal {
    // 定义两个属性
    String name;
    double weight;
    // 定义一个行为(方法)
    public void eat() {
        System.out.println(name + "正在吃饭!现在是" + weight + "kg。");
    }
}
           

4.构造方法

构造方法,也称为构造器,是一种专门用来创建对象的方法,其功能主要是完成对象的初始化,与其他方法一样也可以重载。

public class Person {
    String name;
    int age;

    // 无参构造方法
    public Person() {
    }
    // 有参构造方法
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
           

注意:

  • 构造方法必须和所在类的名字一模一样。
  • 构造方法不需要写返回类型,也不能写void。
  • 如果没有编写任何构造方法,编译器会自动创建一个无参构造方法(可以通过IDEA反编译查看.class文件)。
  • 如果编写了至少一个构造方法,那编译器就不会自动创建无参构造方法,若需要使用无参构造方法,则需要手动添加。

拓展1:值传递和引用传递

值传递:指在调用函数时将实际参数复制一份传递给函数,这样在函数中对参数进行修改,将不会影响到实际参数。

/*值传递*/
public class Demo {
    public static void main(String[] args) {
        int num = 2;
        System.out.println(num);    // 输出结果:2
        change(num);	// 并没有改变实际参数
        System.out.println(num);    // 输出结果:2
    }
    // 无返回值
    private static void change(int a) {
        a = 20;
        System.out.println(a);    // 输出结果:20
    }
}
           

引用传递:指在调用函数时将实际参数的地址传递(而非值)到函数中,这样在函数中对参数所进行的修改,将影响到实际参数。

/*引用传递*/
public class Demo {
    public static void main(String[] args) {
        Student student = new Student();
        // 输出结果:null今年0岁!
        System.out.println(student.name + "今年" + student.age + "岁了!");  
        change(student);
        // 输出结果:小一今年18岁!
        System.out.println(student.name + "今年" + student.age + "岁了!");  
    }
    // 无返回值
    private static void change(Student student) {
        student.name = "小一";
        student.age = 18;
    }
}
// 定义Student类
class Student {
    String name;
    int age;
}
           

拓展2:方法重写

方法重写用于继承关系中,即子类重写父类的方法(名称相同,方法体不同)。当子类需要父类的功能,而子类又有自己的特有内容时,就可以通过方法重写实现,这样,子类即继承了父类的功能,又有自己的特有内容。

/*人类*/
public class Person {
    // 父类方法
    public void sleep (String name) {
        System.out.println(name + "在睡觉!");
    }
}
           
/*学生类*/
public class Student extends Person{
    // 重写父类方法
    public void sleep(String name) {
        System.out.println(name + "在玩手机");
        // 继承父类方法
        super.sleep(name);
    }
}
           
/*测试类*/
public class Test {
    public static void main(String[] args) {
        // 创建对象,调用方法
        Person person = new Person();
        person.sleep("小一");
        System.out.println("-------------");
        Student student = new Student();
        student.sleep("小二");
    }
/*
    运行结果:
    小一在睡觉!
    -------------
    小二在玩手机
    小二在睡觉!
*/
           

注意事项:

  • 方法名必须相同
  • 参数列表必须相同
  • 修饰符的权限范围只增不减(public>protected>default>private)
  • 被static、final、private修饰的方法不能被重写

注解:@Override

作用:帮助检查重写方法的方法声明的正确性。