前几篇文章中我们学习了面向对象的一些基础知识,下面我们来学一下进阶的面向对象!
目录
- static关键字
- 继承
- this、super总结
static
-
什么是static:
- static关键字:表示静态的,是Java中的一个修饰符,可以修饰成员方法,也可以是成员变量。
- 被static修饰的成员变量, 叫做静态变量
- 被static修饰的成员方法,叫做静态方法
-
静态变量的特点:
-
特点:
被该类所有对象共享。(所有的对象都共用同一个值)
不属于对象,属于类。
随着类的加载而加载,优先于对象存在
-
调用:
类名调用:
static.teacherName = "值";或 对象名调用 (不常用)
-
-
静态方法的特点:
-
特点:
多用在测试类和工具类中
JavaBean类中很少会用
-
调用:
类名调用:
ArrayUtil.printArrr(arr);或 对象名调用 (不常用)
-
-
JavaBean类:用来描述一类事物的类。
-
测试类:用来检查其他类是否书写正确的类,带有main方法的类,是程序的人口。
-
工具类:可以帮助我们做一些事情的,但是不描述任何事物的类。
前面我们接触了不少的测试类和JavaBean类,下面我们来学一下工具类吧:
-
注意:
- 需要创建私有化构造方法。目的为了不让外界创建他的对象。
- 方法定义为静态。
例如:
下面我们来实操一下吧:
- 定义学生工具类
需求:定义一个集合,用于存储3个学生对象。 学生类的属性为:name,age,gender。 定义一个工具类,用于获取集合中最大学生的年龄。
首先定义学生的JavaBean类:
private String name; private int age; private String gender;然后创建一个测试类并创建一个集合:
// 1.创建集合用来存储学生对象 ArrayList<Student> list = new ArrayList<>();创建3个学生对象并添加到集合中:
// 2.创建3个学生对象 Student stu1 = new Student("张三",19,"男"); Student stu2 = new Student("李四",18,"女"); Student stu3 = new Student("王五",120,"男"); // 3.把学生对象添加到集合当中 list.add(stu1); list.add(stu2); list.add(stu3);接着创建一个工具类:
// 私有化构造方法 // 目的:为了不让外界创建它的对象 private StudentUtil(){}定义一个求最大值的静态方法
// 静态方法 public static int getMaxAgeStudent(ArrayList<Student> list){ // 1.定义一个参照物,默认0索引的学生年龄最大 int max = list.get(0).getAge(); // 2.循环遍历集合 for (int i = 1; i < list.size(); i++) { // i索引 list.get(i)元素/学生对象 我们还需要getAge获取到年龄之后在进行比较 int tempAge = list.get(i).getAge(); if(tempAge > max){ max = tempAge; } } return max; }调用工具类中的方法:
// 4.调用工具类中的方法 int maxAgeStudent = StudentUtil.getMaxAgeStudent(list); System.out.println(maxAgeStudent);好啦,我们到这里就写完了,我们来运行看一下吧
怎么样,是不是通过工具类中的方法获取出来了呢!下面我们来说一下static的注意事项吧!
-
static注意事项:
-
静态方法只能访问静态变量和静态方法
-
非静态方法可以访问静态变量或者静态方法,也可以访问非静态的成员变量和非静态的成员方法。
-
静态方法中是没有this关键字的。(this表示当前方法调用者的地址值)
-
总结一下:
- 静态方法中只能访问静态。
- 非静态方法可以访问所有
- 静态方法中没有this关键字。
-
静态:是随着类的加载而加载。
-
非静态:跟对象有关的。
-
继承
什么是继承
面向对象的三大特征:封装、继承、多态
封装:对象代表什么,就得封装对应的数据,并提供数据对应的行为。
前面我们学过了封装,下面我们来学一下继承吧!我们先来看一张图片:
这张图片是什么意思呢?其实呀就是定义了三个JavaBean类,而Person类中的属性是Student和Teachar类都需要用到的属性,那么就可以继承给他们两个用,这就是继承。
- 继承:Java中提供了一个关键字extends,用这个关键字我们可以让一个类和另外一个类建立起继承关系。
- 例如
public class Student extends Person{} - Student称为子类,Person称为父类。
- 使用继承的好处:
- 可以把多个子类中重复的代码抽取到父类中,提高代码的复用性。
- 子类可以在父类的基础上增加其他的功能,使子类更强大。
- 使用继承的好处:
- 可以让子类跟类之间产生子夫关系。
- 当类与类之间,存在相同的内容,并满足子类是父类中的一种,就可以考虑使用继承来优化代码。
- 继承的格式:
- public class 子类 extends 父类{}。
- 继承后子类的特点:
- 子类可以得到父类的属性和行为,子类可以使用
下面我们来了解一下继承的特点吧:
继承的特点
-
继承的特点
- Java只支持单继承,不支持多继承,但支持多层继承。
- 单继承:
- 一个子类只能继承一个父类。
- 不支持多继承:
- 子类不能同时继承多个父类。
- 多层继承:
- 子类A继承父类B,父类B可以继承父类C。
- 最大的祖类:
- 每一个类都直接或者间接的继承于Object。
为什么不能多继承呢?我们来看一个例子:
父类A:
父类B:
子类:
因为子类不知道该用谁,使用就不支持多继承。下面我们来看一张多继承的图片你就明白啦!
那是不是可以一直继承父类呢?其实不是的,间接父类的最大类是Object。我们来看一下Java的继承体系吧!
-
下面我们来总结一下吧:
1:Java中只能单继承:一个类只能继承一个直接父类。
2:Java不支持多继承,但是支持多层继承。
3:Java中所有的类都直接或间接继承于Object类。
4.子类只能访问父类中非私有的成员
下面我们来实操一下吧:
定义间接父类行为:
// 父类A
public class AnimalParent {
// 不能加private修饰符
// 子类只能访问父类中非私有的成员方法
public void eat(){
System.out.println("吃东西");
}
public void drink(){
System.out.println("喝水");
}
}
定义父类行为:
// 父类Dog这个类继承父类A的属性 父类Dog
public class Dog extends AnimalParent {
public void lookHome(){
System.out.println("狗在看家");
}
}
定义子类行为:
// 子类TeddyDog这个类继承父类Dog的属性 泰迪子类
public class TeddyDog extends Dog {
public void touch(){
System.out.println("泰迪又在蹭我的腿了!");
}
}
下面在测试类中调用:
// 2.创建泰迪对象
TeddyDog dog2 = new TeddyDog();
dog2.eat(); // 间接父类A的eat行为
dog2.drink(); // 间接父类A的drink行为
dog2.lookHome(); // 继承父类的lookHome行为
dog2.touch(); // 继承子类的touch行为
我们来运行看一下:
怎么样,是不是奇葩的知识又增进了一点呢?
子类到底能继承父类中哪些内容
子类到底能继承父类中哪些内容呢?我们来看一张总结图:
- 我们来总结一句话吧:
- 构造方法:父类不管什么修饰符,子类都不能继承。
- 成员变量:父类不管什么修饰符,子类都可以被继承下来。但是私有的变量需要用到set和get方法。
- 成员方法:父类的虚方法子类不能继承(有private、static、final都不能),简称私有方法都不能,非私有的成员方法子类能继承下来。
继承中的特点
继承中:成员变量的访问特点
成员变量的访问特点:采取就近原则:谁离我近我就用谁
先在局部变量去找,局部变量没有会到本类变量位置去找,父类位置去找,逐级往上。
我们来看段代码吧:
public class Test {
public static void main(String[] args) {
Zi z = new Zi();
z.zishow();
}
}
//创建父类
class Fu{
String name = "Fu";
}
//创建子类
class Zi extends Fu{
String name = "Zi";
public void zishow(){
String name = "zishow";
System.out.println(name); // zishow
// this表示本类,会从本类变量中去找
System.out.println(this.name); // Zi
// super表示父类,会从父类变量中去找
System.out.println(super.name); // Fu
}
}
继承中:成员方法的访问特点
成员方法的访问特点:直接调用满足采取就近原则:谁离我近我就用谁
public class Test2 {
public static void main(String[] args) {
Zi2 z = new Zi2();
z.zishow();
}
}
//创建父类
class Fu2 {
public void eat(){
System.out.println("吃米饭");
}
public void drink(){
System.out.println("喝开水");
}
}
//创建子类
class Zi2 extends Fu2 {
public void zishow(){
eat(); // 吃米饭
drink(); // 喝开水
}
}
成员方法会先在本类中查看eat和drink方法,就会调用子类,如果没有,就会调用父类中继承下来的eat和drink方法。
- 继承中成员方法的访问特点:
- this调用:就近原则。
- super调用:直接找父类。
那这个时候就有个问题了,我的朋友不想吃米饭和喝开水,我的朋友想吃米线和喝饮料怎么办呢?难道要在父类重新定义两个方法来写吗?那不是每次改都要重新定义方法吗?其实不是的,我们可以利用方法重写,那什么是重写呢?我们接着看:
方法重写
- 什么时候使用方法重写?
- 当父类的方法不能满足子类的现在需求时,需要进行方法重写。
- 书写格式:
- 在继承体系中,子类出现了和父类中一模一样的方法声明,我们就称子类这个方法时重写方法。
- @Override重写注解
- @Override是放在重写后的方法上,校验子类重写时语法是否正确。
- 加上注解后如果有红色波浪线,表示语法错误。
- 建议重写方法都加@Override注解,代码安全,优雅。
那注释其实和注解是一样的,注释是给程序员看的,而注解是给程序员和虚拟机看的,当虚拟机看到@Override的时候就会知道你是重写方法的,他就会检查你重写的方法是否正确,如果错误的话会出现红色波浪线,我们来看一下错误的情况吧:
当我们的方法名和父类不一样的时候就会看到出现了红色的波浪线,就表示报错了,所以需要注意一定要和父类的方法名一样!
- 方法书写的注意事项和要求:
- 重写方法的名称、形参列表必须与父类的一致。
- 子类重写父类的方法时,访问权限子类必须大于等于父类(暂时了解:空着不写<protected<public)
- 子类重写父类方法时,返回值类型子类必须小于等于父类。
- 建议:重写的方法尽量和父类保持一致。(建议复制父类的方法)
- 只有能被添加到虚方法表中的方法才能被重写
- 方法重写的本质:
- 覆盖虚方法表中的方法。
继承中:构造方法的特点
- 构造方法的访问特点:
- 父类中的构造方法不会被子类继承。因为如果被继承下来子类构造方法的方法名和类名就不一样了。
- 子类中所有的构造方法默认会先访问父类中的无参构造,在执行自己。
- 为什么呢?
- 因为子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据
- 为什么呢?
- 子类初始化之前,一定要调用父类构造方法先完成父类数据空间的初始化。
- 怎么调用父类的构造方法:
- 子类构造方法的第一行语句默认是:super(),不写虚拟机会自动加载,且必须在第一行。
- 如果想调用父类的有参构造,必须手动写super进行调用。
下面我们来实操一下吧:
如果不写在第一行就会报错:
下面我们通过空参构造创建对象:
下面我们来运行看一下吧:
是不是父类在子类前面呀!那下面我们想要创建的时候赋值怎么办呢?我们接着看:
- 下面我们来说一下构造方法的访问特点吧:
- 子类不能继承父类的构造方法,但是可以通过super调用。
- 子类构造方法的第一行,有一个默认的super();
- 默认先访问父类中无参的构造方法,在执行自己。
- 如果想要父类的有参构造,必须手动书写。
this、super总结
- this:理解为一个变量,表示当前方法调用者的地址值。
- super:代表父类存储空间
| 关键字 | 访问成员变量 | 访问成员方法 | 访问构造方法 |
|---|---|---|---|
| this | this.成员变量(访问本类成员变量) | this.成员方法(参数)(访问本类成员方法) | this(参数)(访问本类构造方法) |
| super | super.成员变量(访问父类成员变量) | super.成员方法(参数)(访问父类成员方法) | super(参数)(访问父类构造方法) |
下面我们来看个案例吧:
-
带有继承结构的标准Javabean类
1.经理
成员变量:工号,姓名,工资,管理奖金。
成员方法:工作(管理其他人),吃饭(吃米饭)。
2.厨师
成员变量:工号,姓名,工资
成员方法:工作(炒菜),吃饭(吃米饭)
由于经理和厨师都有共同的属性:工号和姓名,工资属性,使用可以创建一个父类用来存放:
```
private String id;
private String name;
private double salary;
// 空参构造方法
public Employee() {
}
// 带全部参数构造方法
public Employee(String id, String name, double salary) {
this.id = id;
this.name = name;
this.salary = salary;
}
public void work(){
System.out.println("员工在工作!");
}
public void eat(){
System.out.println("吃米饭!");
}
```
经理类:由于厨师的工作是管理工人,所以需要重写他的工作方法。
// 经理类
private double bouns;
// 空参构造方法
public Manager() {}
// 带全部参数构造方法 (父类+子类)
public Manager(String id, String name, double salary, double bouns) {
super(id, name, salary); // 自动继承父类的参数
this.bouns = bouns;
}
// 重写父类的工作方法
@Override
public void work(){
System.out.println("正在管理其他人!");
}
厨师类:由于厨师的工作是炒菜,所以需要重写他的工作方法。
// 厨师类
public Cook() {
}
public Cook(String id, String name, double salary) {
super(id, name, salary);
}
// 重写父类的工作方法
@Override
public void work(){
System.out.println("正在炒菜");
}
然后在测试类中创建对象并赋值调用他们各自的方法:
// 创建经理对象并赋值调用
Manager m = new Manager("zhangsan001","张三",12000,8000);
System.out.println(m.getId() + "," + m.getName() + "," + m.getSalary() + "," + m.getBouns());
// 调用工作和吃饭方法
m.work();
m.eat();
System.out.println("==========================================");
// 创建厨师对象并赋值调用
Cook c = new Cook("lisi001","李四",8000);
System.out.println(c.getId() + "," + c.getName() + "," + c.getSalary());
// 调用工作和吃饭方法
c.work();
c.eat();
我们来运行看一下吧:
好啦,到这里static、继承、重写就学习完毕啦,有不懂的可以在评论区评论互相讨论哟,我们下期不见不散!
==最后非常感谢您的阅读,也希望能得到您的反馈 ==