final关键字:
-
用final关键字修饰的变量,它的值不允许更改,修饰的类不允许继承,修饰的方法不准许重写
-
使用规范,一般类用final修饰,里面的方法就不用修饰成final方法
-
final修饰普通变量,有三个地方:
-
初始化时定义赋值
private final double PI = 3.14;
-
普通代码块赋值
public final int a; { a =10; }
-
构造器赋值
private final double R; public Circle(double R) { this.R = R; }
-
-
final修饰静态变量,有两个地方:
-
初始化定义赋值
-
静态代码块赋值
-
这个理解着记就好了,因为是final,修饰的变量不允许重新赋值,那么就只有一次赋值的机会,想想有哪些一次赋值的机会,初始化,构造器,代码块(相当于对构造器的补充),静态变量和非静态变量是分开的,就记住啦~
Tips:final和static搭配使用,在初始化时赋值,甚至连类都不用加载(节省点性能哦~),就能在外部调用它,如图
这个还是挺有趣的一个小知识~,接下来事抽象方法和类:
抽象方法:
理解:父类确定子类的方法,但是方法具体执行什么内容不清楚,但是要求一定有子类的继承并重写该方法时,使用抽象方法和抽象类。抽象类的更多作用是设计,设计者设计好以后,让子类继承并实现它
使用方法:在类和要抽象的方法前加上abstract关键字:
abstract class Employee{
public abstract void work();
}
使用细节:
-
抽象类不能被实例化
-
抽象类里可以没有抽象方法,但抽象方法所在类一定是抽象类
-
abstract修饰类和方法,不修饰其他。
-
子类继承抽象类之后,必须重写其中所有抽象方法,除非它自己本身也是抽象类
abstract class Employee{ public abstract void work(); } class Manager extends Employee{ //继承父类方法 @Override public void work() { System.out.printf("经理"+this.name+"工作中"); } } abstract class Manager extends Employee{ //定义为抽象类也不会报错,不过这种抽象继承抽象的写法非常少见 }
-
抽象方法不能用private, final, static修饰,因为这些关键字逻辑上都是和重写相违背的。
理解:抽象方法本质是让子类重写方法体,private权限太高,子类继承后连访问都不可以,更别说重写了。final不必解释。关键的static,我的理解是,static设计出来就是给各个实例化的对象共享通用的,修饰方法时更多的是对静态成员进行操作,无法访问非静态成员。因为没有对实例化后的对象里的普通成员有相关操作,所以基本上用static修饰的方法不需要重写。而abstract修饰的抽象方法本意就是重写,两者冲突了。再者说,用static修饰的方法可以用 类名.方法名 直接调用,而 abstract修饰的方法没有方法体,两个在一起用的话,语法上的 类名.方法名 调用,岂不是调了个空方法体的方法?没有意义
学完了抽象类,但是觉得很难用上?接下来阿飞就和大家一起来看看它怎么用
抽象类实践-模板设计模式:
先来看看需求:
怎么写?先来最简单的方法,每个类,每个不一样的方法都写上一遍:
定义两个类,AA和BB,两个类里都有不同的任务,分别给每个任务计时:
public class AA{
//计算 1+....+ 800000 的时间
public void job() {
//得到开始的时间
long start = System.currentTimeMillis();
long num = 0;
for (long i = 1; i <= 800000; i++) {
num += i;
}
//得的结束的时间
long end = System.currentTimeMillis();
System.out.println("AA 第一个任务执行时间 " + (end - start));
}
//计算 1+....+ 1000000 的时间
public void job2() {
//得到开始的时间
long start = System.currentTimeMillis();
long num = 0;
for (long i = 1; i <= 1000000; i++) {
num += i;
}
//得的结束的时间
long end = System.currentTimeMillis();
System.out.println("AA 第二个任务执行时间 " + (end - start));
}
}
public class BB {
//计算 1+....+ 1200000 的时间
public void job() {
//得到开始的时间
long start = System.currentTimeMillis();
long num = 0;
for (long i = 1; i <= 1200000; i++) {
num += i;
}
//得的结束的时间
long end = System.currentTimeMillis();
System.out.println("BB 第一个任务执行时间 " + (end - start));
}
//计算 1+....+ 1400000 的时间
public void job2() {
//得到开始的时间
long start = System.currentTimeMillis();
long num = 0;
for (long i = 1; i <= 1400000; i++) {
num += i;
}
//得的结束的时间
long end = System.currentTimeMillis();
System.out.println("BB 第二个任务执行时间 " + (end - start));
}
}
乍一看,每个方法都有重复的代码,太过冗余了。我们尝试着把它精简一下:
将每个类中相同逻辑的语句提取到一个calculateTime方法里,然后在那个方法里选择调用要计时的任务
public class AA{
public void calculateTime() {
//得到开始的时间
long start = System.currentTimeMillis();
//选择要计时的任务:如果还有更细致的操作可用一个方法将要执行的
//所有操作封装起来,在这里调用那个方法
job();
//得的结束的时间
long end = System.currentTimeMillis();
System.out.println("任务执行时间 " + (end - start));
}
//计算 1+....+ 800000 的时间
public void job() {
long num = 0;
for (long i = 1; i <= 800000; i++) {
num += i;
}
}
//计算 1+....+ 1000000 的时间
public void job2() {
long num = 0;
for (long i = 1; i <= 1000000; i++) {
num += i;
}
}
}
public class BB {
public void calculateTime() {
//得到开始的时间
long start = System.currentTimeMillis();
job();
//得的结束的时间
long end = System.currentTimeMillis();
System.out.println("任务执行时间 " + (end - start));
}
//计算 1+....+ 1200000 的时间
public void job() {
long num = 0;
for (long i = 1; i <= 1200000; i++) {
num += i;
}
}
//计算 1+....+ 1400000 的时间
public void job2() {
long num = 0;
for (long i = 1; i <= 1400000; i++) {
num += i;
}
}
}
这样的话代码就精简很多了,至少同一个类中的任务,不用每一个任务都加上计时的代码,不错吧?但是......
有没有发现,尽管同一个类中的不同任务不需要写相同的计时,但是不同的类,还是要重复写计时逻辑代码,它要是有一百个类,那岂不是要写上一百遍!?
这时就引出我们的抽象方法了,相同的逻辑,我们可以再提取一遍。这次把计时的逻辑提取到父类,让AA和BB去继承它,留出一个抽象的方法,让子类在里面自由发挥,就像这样:
abstract public class Template { //抽象类-模板设计模式
public abstract void TotalJob();//抽象方法
public void calculateTime() {//实现方法,调用TotalJob方法
//得到开始的时间
long start = System.currentTimeMillis();
TotalJob();
//得的结束的时间
long end = System.currentTimeMillis();
System.out.println("任务执行时间 " + (end - start));
}
}
public class AA extends Template {
@Override
public void TotalJob() {//AA这里计时两个任务
job();
job2();
}
//计算任务
//1+....+ 800000
public void job() { //实现Template的抽象方法job
long num = 0;
for (long i = 1; i <= 800000; i++) {
num += i;
}
}
//计算任务
//1+....+ 1000000
public void job2() {
long num = 0;
for (long i = 1; i <= 1000000; i++) {
num += i;
}
}
}
public class BB extends Template {
@Override
public void TotalJob() {//BB这里计时一个任务
job();
}
//计算 1+....+ 1200000 的时间
public void job() {
long num = 0;
for (long i = 1; i <= 1200000; i++) {
num += i;
}
}
//计算 1+....+ 1400000 的时间
public void job2() {
long num = 0;
for (long i = 1; i <= 1400000; i++) {
num += i;
}
}
}
现在,代码是不是看起来好多了?至少计时的逻辑只在父类出现过一次,然后全局通用。留出抽象的方法也可以在不同的子类里写不同的逻辑。这是基于抽象方法的一种模板设计模式,感觉吧还是挺重要的。参照B站韩老师的视频
~
~
~
我是阿飞,Java路上的萌新,希望和大家多多交流,期待和大家一起进步。完结撒花 ~ ~ ~ ~