面向对象的三个基本特征是:封装、继承、多态。
封装
封装就是对把客观事情封装成抽象了,并且将自己的数据和方法让可信对象操作,对不可信的进行信息的隐藏,以保证信息的安全性。
封装的好处
- 保证数据的安全性,防止调用者随意修改数据。
- 提高组件的重用性,把公用功能放到一个类中,若需要访问某个属性,提供公共方法对其访问即可。
如何进行封装
(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
}
}
输出
我是老师,喜欢教书
我是学生,我爱学习