面向对象的三个基本特征是:封装、继承、多态。

封装

封装就是对把客观事情封装成抽象了,并且将自己的数据和方法让可信对象操作,对不可信的进行信息的隐藏,以保证信息的安全性。

封装的好处

  • 保证数据的安全性,防止调用者随意修改数据。
  • 提高组件的重用性,把公用功能放到一个类中,若需要访问某个属性,提供公共方法对其访问即可。

如何进行封装

(1)私有化成员变量,使用private关键字修饰;

(2)提供公有的get和set方法,在方法体中进行合理值的判断,使用public关键字修饰;

没有封装会带来的问题

我们写一个Person类

public class Person {
    //定义成员变量
    String name;
    int age;
    String gender;

    public void say() {
        System.out.println("我叫" + name + ",性别:"+gender+",今年" + age + "岁");
    }

}

测试类,两个类在同一个包下

public class PersonDemo {
    public static void main(String[] args) {
        Person person = new Person();
        person.name = "小新";
        person.age = -18;
        person.gender = "男";
        person.say();//我叫小新,性别:男,今年-18岁
    }
}

此时从代码语法上来看,是没有任何问题的,但是从逻辑上来分析人的年龄怎么能是负数呢?造成该问题的根本原因就是:可以随意访问对象中的字段。

那么问题来了,怎么才能限制不能随意访问字段数据呢?

此时,就需要了解访问修饰符了!

访问修饰符作用范围表

作用域

当前类

同package

子孙类

其他package

public





protected




×

friendly/default



×

×

private


×

×

×

符号含义, ✓:可以访问 X:不可访问

(1)public:公共。该类或非该类均可访问。

(2)privte:私有。只有在类的主体中才可访问。该关键字只能修饰属性和方法,不能修饰类。

(3)protected:受保护。该类及其子类的成员均可以访问,同一个包中的类也可以访问。该关键字只能修饰修饰属性和方法,不能修饰类。

(4)friendly/default :默认。不使用修饰符。只有相同包中的类可以访问。

this关键字

this的含义

this代表所在类的当前对象的引用(地址值),即对象自己的引用。

记住 **:**方法被哪个对象调用,方法中的this就代表那个对象。即谁在调用,this就代表谁。

封装后的代码
public class Person {
    //定义成员变量
    private String name;
    private int age;
    private String gender;

    public void say() {
        System.out.println("我叫" + name + ",性别:" + gender + ",今年" + age + "岁");
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age < 0) {
            System.out.println("非法的年龄");
            return;
        }
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }
}
public class PersonDemo {

    public static void main(String[] args) {
        Person person = new Person();
        person.setName("小新");//报错:'name' has private access in 'com.java.domain'
        person.setAge(-18);//报错:'age' has private access in 'com.java.domain'
        person.setGender("男");//报错:'gender' has private access in 'com.java.domain'
        person.say();
    }

}

使用private修饰字段后,我们在测试类中不能再操作这些字段了,它会报无权限访问,那怎么办?

此时就要对需要访问的成员变量,提供公共的访问方式,也就是定义对应的 getXxx 方法 、 setXxx 方法。

public class PersonDemo {
    
    public static void main(String[] args) {
        Person person = new Person();
        person.setName("小新");
        person.setAge(-18);
        person.setGender("男");
        person.say();
    }
    
}

输出:

非法的年龄
我叫小新,性别:男,今年0岁

在setAge方法中,我们对age做了校验,所以不会再出现年龄为负的问题。

我们也可以用构造方法用来初始化对象,给对象的成员变量赋初始值。

在Person类下创建一个有参构造方法

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

测试类

public class PersonDemo {

    public static void main(String[] args) {
        Person person = new Person("小新",18,"男");
        person.say();//我叫小新,性别:男,今年18岁
    }

}

继承

继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。

继承的好处
  • 减少代码的冗余,提高代码的复用性。
  • 便于功能的扩展
Java继承的特点
  • java只允许单继承,即子类只能继承一个父类
  • 子类拥有父类的非private属性和方法
  • 子类可以拥有自己的属性和方法
  • 子类可以重写覆盖父类的方法
  • 构造方法是不能被继承的,但可以被子类调用
  • 调用父类构造方法的super()语句必须写在子类构造方法的第一行,否则编译时将出现错误信息
继承的缺点
  • 打破封装,向子类暴露了实现细节 ,因为继承使得父类的方法和属性对子类是透明的,安全性不高
  • 父类更改之后子类也要同时更改
  • 继承是一种强耦合关系
哪些类不能被继承

被final修饰的类不能被继承。java中常见的不能被继承的类有:String,StringBuffer,StringBuilder,以及基本类型的包装类Double,Integer,Long等。

什么情况下使用继承?

当需要用到向上转型,即子类到父类的向上转型,可考虑用继承,非这种情况下,慎用继承。

继承的实现

通过 extends 关键字,可以声明一个子类继承另外一个父类

Employee类:

public class Employee {
    public String name;//姓名
    public int age;//年龄
    private String gender;//性别

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

}

Teacher类:

public class Teacher extends Employee {

    String course;

    public void printMessage() {
        System.out.println("public name:" + name);
        System.out.println("public age:" + age);
        //System.out.println("public gender:"+gender);  不能直接访问私有属性
        System.out.println("public gender:" + getGender());
        System.out.println("我叫" + name + ",今年" + age + "岁,性别:" + getGender() + ",是一名" + course + "老师");
    }

}

测试类:

public class Demo {

    public static void main(String[] args) {
        Teacher teacher = new Teacher();
        teacher.age = 18;
        teacher.name = "小新";
        teacher.course = "电脑";
        teacher.setGender("男");
        teacher.printMessage();
    }
}

输出:

public name:小新
public age:18
public gender:男
我叫小新,今年18岁,性别:男,是一名电脑老师

结果说明,子类能直接继承父类的public和protect成员变量,但并不能直接继承私有变量,但能通过setter和getter方法来访问private变量。

多态

多态的概述

多态是继封装、继承之后,面向对象的第三大特性。

多态:同一行为对不同类的对象产生不同的响应。

生活中,比如跑的动作,小猫、小狗和大象,跑起来是不一样的。再比如飞的动作,昆虫、鸟类和飞机,飞起来也是不一样的。可见,同一行为,通过不同的事物,可以体现出来的不同的形态。多态,描述的就是这样的状态。

多态存在的三个必要条件
  • 要有继承
  • 要有重写
  • 父类引用指向子类对象
多态的体现
父类类型 变量名 = new 子类对象;
变量名.方法名();

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,执行的是子类重写后方法。

代码举例:

Person类

public class Person {

    public void say() {
        System.out.println("我爱中国");
    }

}

Teacher类

public class Teacher extends Person {

    public void say() {
        System.out.println("我是老师,喜欢教书");
    }

}

Student类

public class Student extends Person {

    public void say() {
        System.out.println("我是学生,我爱学习");
    }
    
}

测试类

public class PersonDemo {

    public static void main(String[] args) {
        // 多态形式,创建对象
        Person teacher = new Teacher();
        teacher.say(); // 调用的是 Teacher 的 say
        // 多态形式,创建对象
        Person student = new Student();
        student.say();// 调用的是 Student 的 say
    }
}

输出

我是老师,喜欢教书
我是学生,我爱学习