p101-112 static关键字 继承 final关键字

p101 面向对象高级 static关键字的特点

static关键字介绍:

static是静态的意思,可以修饰成员变量和成员方法。

被其修饰的成员(成员变量和成员方法)会具有的特点:

1.被类的所有对象所共享;

例如创建一个Student类,里面定义了一个static String school来存储学生信息,那么在调用时对它进行一次赋值会导致它存在的这个类里的所有对象的这个变量(无论比它先创建还是后创建)都会是这个赋值后的数据。

2.多了一个调用方式,可以通过类名进行调用

例如Student类里static String school储存学生信息,并且创建了一个对象stu归属于这个类,那么这个school不仅可以使用对象名.变量名的形式(stu.school)来调用,也可以直接通过类名(Student.school)来调用。

在使用static修饰的成员变量时使用类名.成员变量名的形式更为直观,更加推荐使用。

3.随着类的加载而加载,优先于对象存在

在创建这个类的对象之前被static修饰的成员变量就可以被使用。

p102 static对于成员变量的使用场景和内存中的原理

如何判断成员变量是否需要用到static?

判断这个数据是否需要被这个类的所有对象共享

从内存角度理解static关键字:

在类被调用进入方法区的时候,系统就会在堆内存的这个类的静态成员变量区开辟一块空间来存放这个static修饰的(静态的)变量并为其赋予一个默认值(根据数据类型变化,一般是0或者null)

也就是说,只要类被加载、这个类的字节码文件进入方法区,这个类中被static修饰的静态的变量就会在堆内存的这个类对应的静态成员变量区出现。

p103 static对成员方法 修饰类 使用注意事项

static对于成员方法一般在创建工具类的时候会被用到。

案例1:编写一个类ArrayTools,内部编写三个方法

1.从数组中找最小值;2.从数组中找最大值;3.打印数组中的所有元素,打印格式如:[11, 22, 33]

public class ArrayTools {
    public static int findMaximal(int[] array) {
        int number = array[0];
        for (int i = 1; i < array.length; i++) {
            if (number < array[i]) {
                number = array[i];
            }
        }
        return number;
    }
​
    public static int findMinimum(int[] array) {
        int number = array[0];
        for (int i = 1; i < array.length; i++) {
            if (number > array[i]) {
                number = array[i];
            }
        }
        return number;
    }
​
    public static void checkArray(int[] array) {
        System.out.print("[");
        for (int i = 0; i < array.length-1; i++) {
            System.out.print(array[i]+", ");
        }
        System.out.println(array[array.length-1]+"]");
    }
}

像这种本身不创建对象,只提供方法用作处理数据的工具的类就称作工具类。

补充:工具类一般要对构造方法私有化防止创建该类的对象,以节省空间。以上面的工具类为例,只需要添加:

private ArrayTools(){}即可。

回归结论:static关键字用于成员方法的时候一般是制作工具类的时候,可以让使用者调用其成员方法更加简单快捷(无需创建对象,直接用 类名.成员方法 的形式就可以调用成员方法)。

static使用注意事项:
1.static方法中只能访问静态成员

结合内存来理解的话就是,静态方法和静态成员变量无需创建对象就已经出现在内存中可用,非静态的数据只有创建对象之后才可用,所以静态方法无法调用非静态的成员变量。

2.static方法中不能出现this关键字

this关键字代表的是当前对象里数据的引用,所以跟前一条的理由相同,不能出现。

重新认识main方法:

以hello world为例:

public class helloworld {
    public static void main(String[] args) {
        System.out.println("hello world!");
    }
}

public关键字:被jvm调用,需要权限最大,所以使用public

static:被jvm调用,不能创建对象。因为main方法是静态的,所以跟main方法平级的方法也得是static静态的

void:被jvm调用,不需要给虚拟机返回值

main:一个通用的方法名,不是关键字,但是虚拟机只认这个名字

String[] args:以前用来接收键盘录入数据用的,现在没用(能改args但是不能删)(改了也没啥用还不如就这么放着)

p104 继承

什么是继承?

让类和类之间产生关系,子类可以直接使用父类的非私有的成员

继承的格式:

public class 子类名 extends 父类名{}

父类也被称为基类、超类;子类也被称为派生类

创建类的细节:

一个.class文件里可以同时有多个类(class),但是这种写法实际上不便于管理、不符合规范,而且还有一些限制:

1.类和类之间要平级,不平级的话会成为内部类,创建对象格式会变的麻烦很多(内部类的详细后面会有)

2.平级的class中只有一个可以是public修饰(在学习初提到过public会使得这个类的名称要和它所在的文件的文件名相同,所以有两个public之后会报错);

补充:一个JavaBean里用来存储数据的成员变量都是私有的,继承后也无法直接进行操作,但是JavaBean中的set和get方法是公共的,所以一个类继承一个JavaBean之后可以使用里面的set和get方法来进行数据的对应操作

继承的好处:可以提高代码的复用性。

什么时候使用继承?

当类与类之间,存在相同(共性)的内容,并且产生了类似a、b、c都是d的一种的关系,就可以考虑使用继承来优化代码。

举例:学生有姓名、年龄、年级等属性,老师有姓名、年龄、教学班级等属性,就可以向上提取出一个共同的父类“人”,包含姓名、年龄等子类都会出现的相同的内容。

关于继承的学习历程:

1.要知道什么是继承、什么时候使用继承;

2.明确继承中的成员的访问特点:

成员变量

成员方法

构造方法

p105 继承中的成员的访问特点

思考:子类和父类中如果出现了重名的成员变量,会优先使用哪一个?

按照就近原则,选近的,在这种场合也就是先选取子类的

这种情况怎么调用父类的成员变量?

使用super关键字即可,使用形式为 super.变量名。

这里可以联想到以前提到的this关键字,super关键字的作用是在子类和父类变量名相同时候选择调用父类数据,this关键字的作用是成员方法的临时变量名与本类中变量名相同时调用类中的数据

思考2:子类和父类中出现了返回值相同、传参数量类型等相同、方法名相同的两个方法,会优先使用哪一个?

会优先选取子类的,但是这里的原理不是就近原则,而是子类中的这个方法对父类中的这个方法进行了重写

p106 方法重写

什么是方法重写?

子类和父类中出现了方法声明完全相同的方法,就代表子类对父类中的这个方法进行了重写。

注意:方法重载(overload)是方法名相同,参数不同(顺序、数量、类型等等,只要不是完全相同),与返回值无关。

如何判断一个方法是否是重写的方法?

方法1:自己判断方法的声明是否相同(废话)

方法2:在方法的上面加一个@Override 如果这个东西不报错就代表这个方法是一个重写方法(用到了注解,但是这里不赘述,后面有)

什么时候需要用到重写?

当子类需要继承父类并且用到了父类里出现过的方法,但是同时需要对这个方法的逻辑做出修改或者增强,就可以使用重写。

注意:1.可以在子类的方法里包含父类的方法(super.方法名),再添加新的逻辑,这就是上面说的方法的增强。

2.父类中私有的方法(private)无法被重写和调用,但是可以在子类中创建新的方法声明相同的方法。

3.子类重写父类中的方法时,重写后的方法的权限必须大于或等于父类的方法。

p107 权限修饰符

以前(p76)提到过,权限修饰符有四个,从小到大分别是私有private,默认default(无需输入),protected,public。

它们分别最大可允许同一个类、同一个包、不同包的子类、不同包的无关类中进行变量和方法的调用。

这里着重说明protected修饰符的情况。

首先,在不同的包中存在调用关系,需要子类所在的文件中导入(import)父类的文件并且子类继承(extends)父类。

protected有一个弊端是,在子类和父类处于不同包、父类中有protected修饰的方法或者变量的情况下,如果创建了测试类并且测试类没有继承父类,那么就算测试类创建了子类的对象也无法从这个对象直接调用父类的被protected修饰的方法和变量,因为这个测试类和父类是不同包下的无关类的关系

因为这层麻烦的关系,所以实际的编码活动中基本用不到protected修饰符来修饰类中的变量和方法。

继承的特点:

Java中继承只支持单继承(一对一),不支持多继承(一个子类不允许有多个父类),但是可以多层继承(一个类的子类也可以作为其他类的父类)

p108 继承中构造方法的特点

首先,构造方法不能被继承。也就是说子类需要自行完成构造方法的编写。

子类在初始化之前需要先完成父类的初始化->子类如何完成父类的初始化?->初始化对象用什么方法?->构造方法

由此可见,子类只要能够访问到父类的构造方法就可以完成父类的初始化。

子类是怎么访问到父类的构造方法的?

实际上,在所有的类的构造方法中都有一行隐藏的super(),它调用的是父类的空参构造方法

那么没有给定父类的类的构造方法中的super()方法会调用什么?

实际上,java中除了Object类本身以外所有的类都直接或者间接的继承了Object类。

案例:

需求:编写两个类,分别是老师类和学生类,老师类中有姓名、年龄成员变量,有teach成员方法,方法效果为打印出“姓名xxx,年龄xxx的老师正在讲课”到控制台;学生类中有姓名、年龄、成绩成员变量,有study成员方法,方法效果为打印出“姓名xxx,年龄xxx,成绩为xxx的学生正在学习”到控制台;限制类中的所有变量的权限为私有(private),并为这两个类设计完善的构造方法。

思路:两个类中有共同的变量 姓名和年龄,所以向上提取出一个Human类包含这两个变量,并且编写成JavaBean(切合实际应用场景中对数据安全的需求),创建完善的构造方法(无参和带参),再让另外两个类继承这个Human类。

快速设计构造方法:

在一个类中,右键点击空白区域,点Generator(生成),选择Constructor(构造方法)并选择要加进构造方法中的变量,可以快速生成该类的构造方法;继承父类的子类也同样可以通过这个方法来编写构造方法,并且可以在生成之前选择以父类的哪个构造方法为基底创建子类的构造方法。

例如在这个需求中,父类Human有姓名(name)和年龄(age)两个成员变量,所以创建了无参构造方法和带两个参数的构造方法,子类Student在此基础上还有一个成绩(grades)变量,在Student类使用Generator生成构造方法时就可以选择父类的构造方法作为基底,选择性的在构造方法中添加进新的变量来创建子类的构造方法。

回到最开始的话题上。我们用以上方法生成的 子类依靠父类的带参构造方法 生成的 子类的构造方法中,可以看到用到了super带参构造方法,即传给子类对象的数据通过super方法传递到了父类,给了父类中的对应数据进行对象的构造。又因为子类继承了父类,也就继承了父类作为JavaBean,内部生成的权限为public的众多getxxx和setxxx方法,所以可以用子类的对象调用父类的get和set方法来获得和存储数据。

个人理解:子类继承父类的变量,使用构造方法时输入了继承来的变量就会使用super方法传递给父类的构造方法,由父类进行初始化。自己的变量就由自己初始化。

(有点绕 说的更简单的话就是 继承父类变量加进构造方法的话,在构造的时候就要把对应的数据传给父类的构造方法)

p109 继承中的构造方法执行流程-内存流程

以如下情境为例:

父类Person:有两个私有成员变量,private String name(名字)和 private int age(年龄),有一个无参构造方法和一个带参构造方法;

子类Student:有一个成员变量score(分数)并且继承了父类,有一个带有全部三个参数的构造方法。

测试用例:使用带有全部三个参数的构造方法创建一个子类对象,并输出该对象的全部信息。

子类的一次带参构造方法的一次运行流程在内存中的顺序如下:

主函数进栈内存运行;

主函数运行到构造方法,构造方法进入栈内存;

new一个子类对象,在堆内存中开辟空间;

在空间中划分一小块来存放this,存放的内容是这个空间本身的地址;

类自身拥有一个成员变量,空间中划分一小块来存放这个变量;(子类接收并存放自己的数据)

由于继承了父类的两个变量,空间中同时会已有一块专门的区域 super 来存放继承来的变量;

(在方法层面上这里经历了:

子类把数据用super方法传给父类,也就是调用父类的构造方法,父类的构造方法进入栈内存;

父类接收数据并用父类的构造方法在子类的super区域存放该数据,父类的构造方法弹出栈内存)

注意:这里无论继承来的变量在父类中是什么权限都会有一块空间,因为 就算是私有变量也能够被继承,只是使用层面上无法直接进行调用

数据存放完成之后将这个new的空间的地址返回给构造方法构造的对象,对象创建完成,构造方法出栈。

p110 继承综合练习

案例1

程序员类Coder:

成员变量:姓名、年龄、工资

成员方法:work方法,效果为打印信息,格式如“姓名为xx,年龄为xx,工资为xx的程序员正在编写代码”

项目经理类Manager:

成员变量:姓名 年龄 工资 奖金

成员方法:work方法

姓名为xx、年龄为xx、工资为xx、奖金为xx的项目经理正在分配任务

思路:提取一个员工类Employee,包含二者都有的成员变量和work方法

p111 回看this和super

this:代表本类对象的引用

super:代表父类存储空间的标识

经过对比可以发现,使用这两个关键字调用目标的成员变量和方法的格式是相似的,都是关键字.成员名的形式。(实际上this也可以访问本类的构造方法,但是构造方法不会被继承,所以很少用到)

super在子类中没有 和父类相同声明的方法和变量时可以省略,但是按本质来说,这里省略的不是super,而是this。

个人理解:在子类没有重写方法或者有同名的变量的情况下,由父类继承下来的内容直接成为了子类的一部分,所以子类调用这一部分内容的时候就无需再加上this来特地标注出调用的是自己的一部分。(很绕)

所以在编码的学习过程中,前期可以不对这些super和this关键字做省略,增加代码的可读性,积累一定经验之后再开始考虑这两个关键字的省略规则。

回到构造方法,我们已经知道了super可以用来调用父类的构造方法,实际上this也可以用来调用本类的构造方法。

举例使用场景:

假设项目的第一个版本有一个类A,A中有三个变量(a,b,c),并且在项目中已经以该类创建了若干个对象;在第二个版本中需要在该类中追加一个变量d,但是如果直接修改代码修改构造方法会导致以前创建的对象不符合构造方法的规则而报错

这里介绍一个编写代码时的开闭原则:对功能扩展作开放,对修改代码作关闭,即添加代码增加内容时尽量不要修改原有的已经完善的代码,而是在已有的基础上进行拓展。

在举例中的这种情况就可以用this调用构造方法来对构造方法进行拓展,同时也不会影响到已有的构造方法的内容。例如原有的构造方法是:

public A(int a,int b,int c){
    this.a = a;
    this.b = b;
    this.c = c;
}

加入变量d后,新建的构造方法如下:

public A(int a,int b,int c,int d){
    this.a = a;
    this.b = b;
    this.c = c;
    this.d = d;
}

这样就达成了原有代码不出错并且拓展的效果了,但是可见新的构造方法里有一部分与旧的构造方法内容相同,所以可以省略成以下格式:

public A(int a,int b,int c,int d){
    this(a,b,c);
    this.d = d;
}

这里的this(a,b,c)调用了本类中的构造方法,实际上就产生了和构造方法A(a,b,c)相同的效果。这样省略的优点是可以减少冗余的代码量。

补充:this和super在调用构造方法时候不能共存

p112 final关键字

final关键字是代表最终的修饰符,可以修饰方法、类、变量

final修饰的特点:

修饰方法:代表该方法是最终方法,不能被重写

修饰类:代表该方法是最终类,不能被继承

修饰变量:代表该变量是常量,不能再次被赋值

其中final修饰变量有两种情况,一种是修饰基本数据类型,代表该变量的数据不能改变;另一种是修饰引用数据类型,代表该对象指向的地址不可改变,而该地址指向的地区的内容是可以改变的

final修饰成员变量的注意事项:

final修饰的成员变量不允许是默认值

final修饰的成员变量只能在定义时直接被赋值或者在类的构造方法中被赋值,不能被其他方法赋值;后者比前者更麻烦而且容易出问题,所以建议在定义的时候直接赋值。

final修饰变量的业界内命名规范:

如果修饰的变量名是一个单词,那么所有字母大写;

如果修饰的变量名是多个单词,那么所有字母大写并且使用下划线将各个单词分开。

本章学习目标:

  • 理解static关键字修饰成员的特点; 1.被类的所有对象共享; 2.多了一种调用方式,可以通过类名进行调用; 3.随着类的加载而加载。优先于对象存在。
  • 理解继承的好处,以及继承的使用场景; 继承的好处:提高代码的复用性 使用场景:多个类之间存在共性内容,并且有类似上下包含的关系(子类是父类的某一部分)
  • 清楚继承中成员的访问特点;
  • 能够运用this和super访问到自己想调用的成员;
  • 清楚final修饰方法、类、变量的特点
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值