面向对象
面向对象的特征:
封装
随着系统越来越复杂,类会越来越多,那么类之间的访问边界必须把握好,面向对象的开发原则要遵循“
高内聚、低耦合
”,而“高内聚,低耦合”的体现之一:
高内聚
:类的内部数据操作细节自己完成,不允许外部干涉;低耦合
:仅暴露少量的方法给外部使用,尽量方便外部调用封装可以认为是一个保护屏障,防止该类的代码和数据被其他类随意访问,加强了代码的
安全性
。隐藏对象内部的复杂性,只对外公开简单和可控的访问方式,从而提高系统的可扩展性、可维护性。
- 权限修饰符:
public
、protected
、缺省
、private
。具体访问范围如下:
修饰符 | 本类内部 | 本包内 | 其他包的子类 | 其他包非子类 |
---|---|---|---|---|
private | √ | × | × | × |
缺省 | √ | √ | × | × |
protected | √ | √ | √ | × |
public | √ | √ | √ | √ |
封装的体现
成员变量/属性私有化
私有化类的成员变量,提供公共的get和set方法,对外暴露获取和修改属性的功能。
实现步骤:
private 数据类型 变量名 ;
例:
第一步:使用private修饰成员变量
public class Person {private String name;private int age;private boolean marry;
}
第二步:提供 get\set 方法,可以访问成员变量:
public class Person {private String name;private int age;private boolean marry;public void setName(String n) {name = n;}public String getName() {return name;}public void setAge(int a) {age = a;}public int getAge() {return age;}public void setMarry(boolean m){marry = m;}public boolean isMarry(){return marry;}
}
第三步:测试
public class PersonTest {public static void main(String[] args) {Person p = new Person();//实例变量私有化,跨类是无法直接使用的p.setName("张三");System.out.println("p.name = " + p.getName());p.setAge(23);System.out.println("p.age = " + p.getAge());p.setMarry(true);System.out.println("p.marry = " + p.isMarry());}
}
注意:
开发中,一般成员实例变量都习惯使用private修饰,再提供相应的public权限的get/set方法访问。
对于final的实例变量,不提供set()方法。
对于static final的成员变量,习惯上使用public修饰。
关键字:this
- this在方法内部使用,表示调用该方法的对象
- this在构造器内部使用,表示该构造器正在初始化的对象
注意:使用this访问属性和方法时,如果在本类中未找到,会从父类中查找。
方法的重载
重载:在同一个类中,方法名相同,参数列表不同
特点:与修饰符、返回值类型无关,只看参数列表,且参数列表必须不同(参数的个数或类型)。调用时,根据方法参数列表的不同来区别。
例:
//返回两个整数的和
public int add(int x,int y){return x+y;
}//返回三个整数的和
public int add(int x,int y,int z){return x+y+z;
}
//返回两个小数的和
public double add(double x,double y){return x+y;
}
关键字:static
静态变量
使用static修饰的成员变量就是静态变量(或类变量、类属性)
1.适用范围
在Java类中,可用static修饰属性、方法、代码块、内部类
2.被修饰后成员的特点
- 随着类的加载而加载
- 优先于对象存在
- 修饰的成员,被所有对象所共享
- 访问权限允许时,可不创建对象,直接被类调用
语法格式:
[修饰符] class 类{[其他修饰符] static 数据类型 变量名;
}
3.静态变量的特点:
- 静态变量值是所有对象共享。
- 静态变量的值存储在方法区。
- 静态变量在本类中,可以在任意方法、代码块、构造器中直接使用。
- 静态变量的get/set方法也是静态的
静态方法
1.语法格式
用static修饰的成员方法就是静态方法。
[修饰符] class 类{[其他修饰符] static 返回值类型 方法名(形参列表){方法体}
}
2.静态方法的特点
- 静态方法在本类的任意方法、代码块、构造器中都可以直接被调用。
- 在static方法内部只能访问类的static修饰的属性或方法,不能访问类的非static的结构。
- 静态方法可以被子类继承,但不能被子类重写。
- static方法内部不能有this,也不能有super。
继承
例:
为描述和处理个人信息,定义类Person:
class Person{public String name;public int age;public Date birthDate;
}为描述和处理学生信息,定义类Student:
class Student{public String name;public int age;public Date birthDate;public String school;
}通过继承,可以简化Student类的定义:
class Student extends Person{public String school;
}
说明:Student类继承了父类Person的所有属性和方法,并增加了一个属性school。Person中的属性和方法,Student都可以使用。
继承的好处
- 继承的出现减少了代码冗余,提高了代码的复用性。
- 继承的出现,更有利于功能的扩展。
- 继承的出现让类与类之间产生了
is-a
的关系,提供了多态的前提。- 继承描述的是事物之间的所属关系,这种关系是:
is-a
的关系。可见,父类更通用或更一般,子类更具体。
- 继承描述的是事物之间的所属关系,这种关系是:
继承的语法
通过 extends关键字,可以声明一个类B继承另外一个类A,定义格式如下:
[修饰符] class 类A {...
}[修饰符] class 类B extends 类A {...
}
继承中的基本概念:
类A,称为父类、超类、基类(base class)、SuperClass
类B,称为子类、派生类(derived class)、SubClass
代码举例:
父类:public class Animal {// 定义name属性String name;// 定义age属性int age;// 定义动物的吃东西方法public void eat() {System.out.println(age + "岁的"+ name + "在吃东西");}
} 子类:public class Cat extends Animal {int count;//记录每只猫抓的老鼠数量// 定义一个猫抓老鼠的方法catchMousepublic void catchMouse() {count++;System.out.println("抓老鼠,已经抓了"+ count + "只老鼠");}
} 测试类:public class TestCat {public static void main(String[] args) {// 创建一个猫类对象Cat cat = new Cat();// 为该猫类对象的name属性进行赋值cat.name = "Tom";// 为该猫类对象的age属性进行赋值cat.age = 2;// 调用该猫继承来的eat()方法cat.eat();// 调用该猫的catchMouse()方法cat.catchMouse();cat.catchMouse();cat.catchMouse();}
}
继承的细节说明
1.子类会继承父类所有的实例变量和实例方法
- 父类是所有子类共同特征的抽象描述。而实例变量和实例方法就是事物的特征,那么父类中声明的实例变量和实例方法代表子类事物也有这个特征。
2.子类不能直接访问父类中私有的(private)的成员变量和方法
- 子类虽会继承父类私有(private)的成员变量,但子类不能对继承的私有成员变量直接进行访问,可通过继承的get/set方法进行访问
3.在Java 中,子类不是父类的子集,而是对父类的扩展
- 子类在继承父类以后,还可以定义自己特有的方法,这就可以看做是对父类功能上的扩展。
4.Java中一个类只能有一个父类,不可以有多个直接父类。
- class C extends A,B... ❌
5.Java支持多层继承
-
class A{} class B extends A{} class C extends B{}
6、一个父类可以同时拥有多个子类
-
class A{} class B extends A{} class D extends A{} class E extends A{}
关键字:super
1.super的理解
在Java类中使用super来调用父类中的指定操作:
- super可用于访问父类中定义的属性
- super可用于调用父类中定义的成员方法
注意:
如果子类没有重写父类的方法,只要权限修饰符允许,在子类中可以直接调用父类的方法;
如果子类重写了父类的方法,在子类中需要通过super才能调用父类被重写的方法,否则默认调用的子类重写的方法
例:
public class SmartPhone extends Phone{//重写父类的来电显示功能的方法public void showNum(){//来电显示姓名和图片功能System.out.println("显示来电姓名");System.out.println("显示头像");//保留父类来电显示号码的功能super.showNum();//此处必须加super.,否则就是无限递归,那么就会栈内存溢出}
}
-
方法前面没有super.和this.
- 先从子类找匹配方法,如果没有,再从直接父类找,再没有,继续往上追溯
-
方法前面有this.
- 先从子类找匹配方法,如果没有,再从直接父类找,再没有,继续往上追溯
-
方法前面有super.
- 从当前子类的直接父类找,如果没有,继续往上追溯
2.子类中调用父类中同名的成员变量
- 如果实例变量与局部变量重名,可以在实例变量前面加this.进行区别
- 如果子类实例变量和父类实例变量重名,并且父类的该实例变量在子类仍然可见,在子类中要访问父类声明的实例变量需要在父类实例变量前加super.,否则默认访问的是子类自己声明的实例变量
- 如果父子类实例变量没有重名,只要权限修饰符允许,在子类中完全可以直接访问父类中声明的实例变量,也可以用this实例访问,也可以用super实例变量访问
3.总结
-
变量前面有this
- 通过this找成员变量时,先从当前执行代码的本类去找成员变量
- 如果从当前执行代码的本类中没有找到,会往上找父类声明的成员变量
-
变量前面super
- 通过super找成员变量,直接从当前执行代码的直接父类去找成员变量
- 如果直接父类没有,就去父类的父类中找
-
this
- this.成员变量:表示当前对象的某个成员变量,而不是局部变量
- this.成员方法:表示当前对象的某个成员方法,完全可以省略this.
-
super
- super.成员变量:表示当前对象的某个成员变量,该成员变量在父类中声明的
- super.成员方法:表示当前对象的某个成员方法,该成员方法在父类中声明的
方法的重写
1.重写的概念
重写只发生在继承关系中,方法名、参数列表、返回类型相同,但方法实现的过程不同就称为方法重写。
例:
//假设有一个Animal类,其中有一个makeSound方法。
class Animal {public void makeSound() {System.out.println("Animal makes a sound");}
}
//然后有一个Dog子类,它重写了makeSound方法。
class Dog extends Animal {@Overridepublic void makeSound() {System.out.println("Dog barks");}
}
2.方法重写的要求
- 子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表。
- 子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型。(例如:Student < Person)。
- 子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限。(public > protected > 缺省 > private)注意:父类私有方法不能重写